2012-04-19 33 views
5

Tôi đã làm việc để tạo một trình phân bổ tùy chỉnh làm một bài tập/thực hành thú vị và tôi đã gặp phải hai vấn đề tiềm năng với việc tạo mảng. Đối với một cuộc gọi thông thường để phân bổ, tôi sẽ sử dụng mallocplacement new. Tuy nhiên, khi tôi tạo ra một mảng, tôi đang bối rối về cách nó nên được thực hiện. Đối với một lần, tôi đã nhận thấy ở một số nơi có vẻ như placement new có thể không an toàn cho các mảng như here. Tôi cũng chạy vào một lỗi của riêng tôi trong khi cố gắng sử dụng placement new cho một mảng. Tôi sẽ nhận được lỗi củaCách tạo một mảng trong khi có khả năng sử dụng vị trí mới

'lỗi C2679: nhị phân '=': không có nhà điều hành tìm thấy trong đó có một toán hạng bên phải của loại 'SomeClass *'(hoặc không có chuyển đổi có thể chấp nhận)

Tôi hiểu lỗi (tôi tin) nhưng tôi muốn có lỗi được giải quyết thông qua phương pháp xây dựng mảng của tôi. Tôi có hai câu hỏi

1) Làm cách nào để người cấp phát có thể tạo mảng mà không sử dụng new[]? Là nó với placement new? Nếu vậy, điều gì về những mối nguy hiểm tiềm năng được đề cập từ liên kết tôi đã đăng ở trên?

2) Nếu tôi giả sử sử dụng placement new và gọi nó trên từng phần tử trong mảng, tại sao tôi nhận được lỗi được đề cập ở trên?

#include <stdio.h> 
#include <new> 

class SomeClass{ 
public: 
    SomeClass() { 
     printf("Constructed\n"); 
    } 

    ~SomeClass() { 
     printf("Destructed\n"); 
    } 
}; 

void* SomeAllocationFunction(size_t size) { 
    return malloc(size); 
} 

template<typename Type> 
Type* SomeArrayAllocationFunction(size_t count){ 
    Type* mem = (Type*)SomeAllocationFunction(sizeof(Type) * count); 

    for(unsigned int i = 0; i < count; ++i) 
    { 
     mem[i] = new(mem + i) Type(); 
    } 

    return mem; 
} 

int main(void){ 
    SomeClass* t = SomeArrayAllocationFunction<SomeClass>(2); 
} 

Trả lời

1

1) Làm cách nào để người cấp phát có thể tạo mảng mà không sử dụng [] mới? Có vị trí mới không? Nếu vậy, điều gì về những mối nguy hiểm tiềm năng được đề cập từ liên kết tôi đã đăng ở trên?

Sự cố trong liên kết là hiểu sai cách hoạt động của mọi thứ. Mỗi thực hiện có một thực hiện được định nghĩa có nghĩa là để ghi lại thông tin về một mảng được phân bổ. Thông tin này không cần thiết với các đối tượng đơn lẻ, vì nó được quản lý bởi máy khách thông qua việc thực hiện delete. Với một mảng, việc triển khai phải ghi lại những thứ như số phần tử, hàm hủy để gọi (nếu có), kích thước phần tử… công cụ này thường được lưu trữ ở đầu phân bổ trả về và việc triển khai rõ ràng bù đắp kích thước của yêu cầu phân bổ một cách thích hợp. Do đó, kích thước thực tế được bù đắp để phù hợp với các giá trị ẩn này. Đây là lý do tại sao malloc(sizeof...) sẽ không hoạt động trừ khi người cấp phát của bạn thực hiện thêm sổ sách kế toán (trong đó, btw, std::allocator và giao diện bộ sưu tập mang lại).

Để ghi lại thông tin này một cách chính xác, bạn có thể xác định static void* operator new[]. Để kết hợp cấp phát của riêng bạn trong chương trình này thông qua vị trí, bạn có thể sử dụng phương pháp sau:

// quick/dirty/incomplete illustration: 
#include <stdio.h> 
#include <new> 
#include <cstdlib> 

class t_allocator { 
public: 
    t_allocator() { 
    } 

    ~t_allocator() { 
    } 

public: 
    void* allocate(const size_t& size) { 
     return malloc(size); 
    } 
}; 

class SomeClass { 
public: 
    SomeClass() { 
     printf("Constructed\n"); 
    } 

    ~SomeClass() { 
     printf("Destructed\n"); 
    } 

public: 
    static void* operator new[](size_t size, t_allocator& allocator) { 
     return allocator.allocate(size); 
    } 

    /* in case static void* operator new[](size_t size, t_allocator& allocator) throws: */ 
    static void operator delete[](void* object, t_allocator& allocator) { 
     /* ... */ 
    } 

    static void operator delete[](void* object) { 
     /* matches t_allocator::allocate */ 
     free(object); 
    } 
}; 

int main(void) { 
    t_allocator allocator; 
    SomeClass* t(new (allocator) SomeClass[2]); 

    delete[] t; 
    t = 0; 

    return 0; 
} 

lưu ý rằng bạn sẽ tương tự như thực hiện vị trí operator delete[] nếu cấp phát của bạn có thể ném.

nếu bạn muốn người cấp phát của mình thực hiện một số sổ sách kế toán, việc này trở nên lộn xộn. cá nhân, tôi không nghĩ rằng tình hình này được thực hiện tốt bởi ngôn ngữ, đặc biệt là kể từ khi khởi tạo mảng đã không được thực hiện tốt. sẽ luôn có một bước bổ sung để thực hiện gần việc xây dựng hoặc phá hủy hoặc một số dữ liệu có thể truy cập toàn cầu để sử dụng trong ngữ cảnh này.

2) Nếu tôi giả sử sử dụng vị trí mới và gọi nó trên từng phần tử trong mảng, tại sao tôi nhận được lỗi được đề cập ở trên?

bạn cần phải xây dựng phần tử rõ ràng nếu bạn đang tạo một người cấp phát không qua operator new/operator new[]. mở rộng ví dụ trên, bạn sẽ muốn có phương thức hủy được gọi là delete[], sau đó yêu cầu this giải phóng/sử dụng lại bộ nhớ (thay vì sử dụng free ở trên).

nếu bạn chỉ muốn một giải pháp nhanh chóng, bạn sẽ cần phải lug destructor, kích thước và phần tử đếm xung quanh với phân bổ hoặc cấp phát. trong bối cảnh đó, bạn không sử dụng new[]/delete[].

Sửa

và nếu bạn muốn quản lý những cuốn sách mình, đây là một cách tiếp cận (có thể đi nhiều hướng):

#include <cassert> 
#include <stdio.h> 
#include <new> 
#include <cstdlib> 

class t_allocator { 
public: 
    t_allocator() { 
    } 

    ~t_allocator() { 
    } 

public: 
    /** tracks an array allocation's data. acts as a scope container for the allocation/types. */ 
    class t_array_record { 
    public: 
    typedef void (*t_destructor)(void* const); 

    template<typename T> 
    t_array_record(T*& outObjects, t_allocator& allocator, const size_t& count) : d_mem(allocator.allocate(sizeof(T), count)), d_destructor(t_allocator::t_array_record::Destruct<T>), d_size(sizeof(T)), d_count(count), d_allocator(allocator) { 
     assert(this->d_mem); 
     /* mind exceptions */ 
     char* const cptr(reinterpret_cast<char*>(this->d_mem)); 

     for (size_t idx(0); idx < this->d_count; ++idx) { 
     /* assignment not required here: */ 
     new (&cptr[this->d_size * idx]) T(); 
     } 

     outObjects = reinterpret_cast<T*>(this->d_mem); 
    } 

    ~t_array_record() { 
     assert(this->d_mem); 
     char* const cptr(reinterpret_cast<char*>(this->d_mem)); 

     for (size_t idx(0); idx < this->d_count; ++idx) { 
     const size_t element(this->d_count - idx - 1U); 
     this->d_destructor(& cptr[this->d_size * element]); 
     } 

     this->d_allocator.free(this->d_mem); 
    } 

    private: 
    template<typename T> 
    static void Destruct(void* const ptr) { 
     T* const obj(reinterpret_cast<T*>(ptr)); 

     obj->~T(); 
    } 

    private: 
    void* const d_mem; 
    t_destructor d_destructor; 
    const size_t d_size; 
    const size_t d_count; 
    t_allocator& d_allocator; 
    public: 
    t_array_record(const t_array_record&); 
    t_array_record& operator=(const t_array_record&); 
    }; 
public: 
    void* allocate(const size_t& size, const size_t& count) { 
    return malloc(size * count); 
    } 

    void free(void* const mem) { 
    ::free(mem); 
    } 
}; 

Demo:

class SomeClass { 
public: 
    SomeClass() { 
    printf("Constructed\n"); 
    } 

    virtual ~SomeClass() { 
    printf("Destructed\n"); 
    } 

    virtual void greet() { 
    printf("hi: %p\n", this); 
    } 

private: 
    SomeClass(const SomeClass&); 
    SomeClass& operator=(const SomeClass&); 
}; 

class SomeDer : public SomeClass { 
    static int& N() { 
    static int a(0); 

    return ++a; 
    } 

public: 
    SomeDer() : d_number(N()) { 
    printf("Ctor-%i\n", this->d_number); 
    } 

    virtual ~SomeDer() { 
    printf("~Der%i-", this->d_number); 
    } 

    virtual void greet() { 
    printf("Der%i-", this->d_number); 
    SomeClass::greet(); 
    } 

private: 
    const int d_number; /* << so we have different sized types in the example */ 
}; 

template<typename T> 
void TryIt(const size_t& count) { 
    t_allocator allocator; 

    T* things(0); 
    t_allocator::t_array_record record(things, allocator, count); 

    for (size_t idx(0); idx < count; ++idx) { 
    things[idx].greet(); 
    } 
} 

int main() { 
    TryIt<SomeClass>(3); 
    TryIt<SomeDer>(9); 
    return 0; 
} 
+0

Làm thế nào ví dụ của bạn có thể được thực hiện mà không quá tải [] mới và xóa [] cho lớp học? Tôi đã hy vọng không phải quá tải tất cả các lớp học của tôi cho mới và xóa, nhưng thay vì chỉ sử dụng một cấp phát. – mmurphy

+0

@mmurphy nếu đó là sở thích của bạn, bạn có thể định nghĩa chúng là các toán tử miễn phí thay thế. một cách tiếp cận khác là trong thư ... – justin

+0

@mmurphy thêm bản demo quản lý rõ ràng – justin

1

mem[i] có kiểu Type& khi new(mem + i) Type(); có kiểu Type*. Đây là những loại không tương thích rõ ràng và không thể được chỉ định. Tôi tin rằng bạn có thể xóa bài tập hoàn toàn và bài tập sẽ hoạt động, vẫn đang khởi tạo bộ nhớ tại vị trí đó cho bạn.

Tôi vẫn sẽ hơi cảnh giác với việc triển khai trình phân bổ mảng của riêng bạn mặc dù (một trình phân bổ tùy chỉnh cho vector sẽ rõ ràng hơn chẳng hạn).

+0

Ah vâng, 'new (mem + i) Type();' có vẻ là tất cả những gì cần thiết. Việc sử dụng 'vị trí mới' có nguy hiểm cho việc phân bổ mảng không? Tôi dường như tìm thấy một số nơi mà nó được coi là nguy hiểm. – mmurphy

+0

@mmurphy Nó không nguy hiểm, nếu bạn biết bạn đang làm gì. nó chỉ là không cần thiết trong trường hợp của bạn. – RedX

+0

@RedX: Việc sử dụng vị trí mới không cần thiết trong trường hợp của tôi như thế nào? – mmurphy

Các vấn đề liên quan