2013-05-04 31 views
31

Tôi có một chương trình có chứa một giai đoạn xử lý cần sử dụng một loạt các cá thể đối tượng khác nhau (tất cả được phân bổ trên heap) từ một cây các loại đa hình, tất cả đều bắt nguồn từ một lớp cơ sở chung.C++ 11 mẫu thiết kế bộ nhớ hồ bơi?

Vì các trường hợp có thể tham khảo tuần hoàn lẫn nhau và không có chủ sở hữu rõ ràng, tôi muốn phân bổ chúng bằng new, xử lý chúng bằng con trỏ thô và để chúng trong bộ nhớ cho giai đoạn (ngay cả khi chúng không được tham chiếu), và sau giai đoạn của chương trình sử dụng những trường hợp này, tôi muốn xóa tất cả chúng cùng một lúc.

Làm thế nào tôi nghĩ để cấu trúc nó là như sau:

struct B; // common base class 

vector<unique_ptr<B>> memory_pool; 

struct B 
{ 
    B() { memory_pool.emplace_back(this); } 

    virtual ~B() {} 
}; 

struct D : B { ... } 

int main() 
{ 
    ... 

    // phase begins 
    D* p = new D(...); 

    ... 

    // phase ends 
    memory_pool.clear(); 
    // all B instances are deleted, and pointers invalidated 

    ... 
} 

Ngoài cẩn thận rằng tất cả các trường B được giao mới, và không ai có sử dụng bất kỳ gợi ý cho họ sau khi các hồ bơi bộ nhớ sẽ bị xóa, là có vấn đề với việc thực hiện này?

Cụ thể là tôi quan tâm đến thực tế rằng con trỏ this được sử dụng để xây dựng một std::unique_ptr trong hàm tạo lớp cơ sở, trước khi hàm tạo lớp dẫn xuất đã hoàn thành. Điều này có dẫn đến hành vi không xác định không? Nếu có thì có cách giải quyết khác không?

+0

Tại sao 'B' cần biết về vùng bộ nhớ? Thêm các con trỏ từ mã trình điều khiển ('main') trông giống như nó sẽ vô cùng thích hợp hơn. Và sau đó tất nhiên tuổi thọ của hồ bơi có thể bị giới hạn, điều này có nghĩa là bạn sẽ bị hủy diệt miễn phí khi nó vượt quá phạm vi. – Jon

+0

@Jon: Bạn có nghĩa là thay thế các cuộc gọi đến 'new D (...)' bằng 'memory_pool.emplace_back (new D (...))'? Tôi cho rằng tôi có thể viết một hàm mẫu theo kiểu 'make_shared' để tạo đối tượng, chuyển tiếp các đối số tới hàm tạo và thêm nó vào nhóm bộ nhớ. Tôi vẫn muốn biết nếu mã trên là UB hay không. –

Trả lời

2

Hmm, gần đây tôi cần gần như chính xác (bộ nhớ cho một giai đoạn của một chương trình được xóa cùng một lúc), ngoại trừ việc tôi có thêm ràng buộc thiết kế rằng tất cả các đối tượng của tôi sẽ khá nhỏ.

tôi đã đưa ra sau "hồ bơi bộ nhớ nhỏ đối tượng" - có lẽ nó sẽ được sử dụng cho bạn:

#pragma once 

#include "defs.h" 
#include <cstdint>  // uintptr_t 
#include <cstdlib>  // std::malloc, std::size_t 
#include <type_traits> // std::alignment_of 
#include <utility>  // std::forward 
#include <algorithm> // std::max 
#include <cassert>  // assert 


// Small-object allocator that uses a memory pool. 
// Objects constructed in this arena *must not* have delete called on them. 
// Allows all memory in the arena to be freed at once (destructors will 
// be called). 
// Usage: 
//  SmallObjectArena arena; 
//  Foo* foo = arena::create<Foo>(); 
//  arena.free();  // Calls ~Foo 
class SmallObjectArena 
{ 
private: 
    typedef void (*Dtor)(void*); 

    struct Record 
    { 
     Dtor dtor; 
     short endOfPrevRecordOffset; // Bytes between end of previous record and beginning of this one 
     short objectOffset;    // From the end of the previous record 
    }; 

    struct Block 
    { 
     size_t size; 
     char* rawBlock; 
     Block* prevBlock; 
     char* startOfNextRecord; 
    }; 

    template<typename T> static void DtorWrapper(void* obj) { static_cast<T*>(obj)->~T(); } 

public: 
    explicit SmallObjectArena(std::size_t initialPoolSize = 8192) 
     : currentBlock(nullptr) 
    { 
     assert(initialPoolSize >= sizeof(Block) + std::alignment_of<Block>::value); 
     assert(initialPoolSize >= 128); 

     createNewBlock(initialPoolSize); 
    } 

    ~SmallObjectArena() 
    { 
     this->free(); 
     std::free(currentBlock->rawBlock); 
    } 

    template<typename T> 
    inline T* create() 
    { 
     return new (alloc<T>()) T(); 
    } 

    template<typename T, typename A1> 
    inline T* create(A1&& a1) 
    { 
     return new (alloc<T>()) T(std::forward<A1>(a1)); 
    } 

    template<typename T, typename A1, typename A2> 
    inline T* create(A1&& a1, A2&& a2) 
    { 
     return new (alloc<T>()) T(std::forward<A1>(a1), std::forward<A2>(a2)); 
    } 

    template<typename T, typename A1, typename A2, typename A3> 
    inline T* create(A1&& a1, A2&& a2, A3&& a3) 
    { 
     return new (alloc<T>()) T(std::forward<A1>(a1), std::forward<A2>(a2), std::forward<A3>(a3)); 
    } 

    // Calls the destructors of all currently allocated objects 
    // then frees all allocated memory. Destructors are called in 
    // the reverse order that the objects were constructed in. 
    void free() 
    { 
     // Destroy all objects in arena, and free all blocks except 
     // for the initial block. 
     do { 
      char* endOfRecord = currentBlock->startOfNextRecord; 
      while (endOfRecord != reinterpret_cast<char*>(currentBlock) + sizeof(Block)) { 
       auto startOfRecord = endOfRecord - sizeof(Record); 
       auto record = reinterpret_cast<Record*>(startOfRecord); 
       endOfRecord = startOfRecord - record->endOfPrevRecordOffset; 
       record->dtor(endOfRecord + record->objectOffset); 
      } 

      if (currentBlock->prevBlock != nullptr) { 
       auto memToFree = currentBlock->rawBlock; 
       currentBlock = currentBlock->prevBlock; 
       std::free(memToFree); 
      } 
     } while (currentBlock->prevBlock != nullptr); 
     currentBlock->startOfNextRecord = reinterpret_cast<char*>(currentBlock) + sizeof(Block); 
    } 

private: 
    template<typename T> 
    static inline char* alignFor(char* ptr) 
    { 
     const size_t alignment = std::alignment_of<T>::value; 
     return ptr + (alignment - (reinterpret_cast<uintptr_t>(ptr) % alignment)) % alignment; 
    } 

    template<typename T> 
    T* alloc() 
    { 
     char* objectLocation = alignFor<T>(currentBlock->startOfNextRecord); 
     char* nextRecordStart = alignFor<Record>(objectLocation + sizeof(T)); 
     if (nextRecordStart + sizeof(Record) > currentBlock->rawBlock + currentBlock->size) { 
      createNewBlock(2 * std::max(currentBlock->size, sizeof(T) + sizeof(Record) + sizeof(Block) + 128)); 
      objectLocation = alignFor<T>(currentBlock->startOfNextRecord); 
      nextRecordStart = alignFor<Record>(objectLocation + sizeof(T)); 
     } 
     auto record = reinterpret_cast<Record*>(nextRecordStart); 
     record->dtor = &DtorWrapper<T>; 
     assert(objectLocation - currentBlock->startOfNextRecord < 32768); 
     record->objectOffset = static_cast<short>(objectLocation - currentBlock->startOfNextRecord); 
     assert(nextRecordStart - currentBlock->startOfNextRecord < 32768); 
     record->endOfPrevRecordOffset = static_cast<short>(nextRecordStart - currentBlock->startOfNextRecord); 
     currentBlock->startOfNextRecord = nextRecordStart + sizeof(Record); 

     return reinterpret_cast<T*>(objectLocation); 
    } 

    void createNewBlock(size_t newBlockSize) 
    { 
     auto raw = static_cast<char*>(std::malloc(newBlockSize)); 
     auto blockStart = alignFor<Block>(raw); 
     auto newBlock = reinterpret_cast<Block*>(blockStart); 
     newBlock->rawBlock = raw; 
     newBlock->prevBlock = currentBlock; 
     newBlock->startOfNextRecord = blockStart + sizeof(Block); 
     newBlock->size = newBlockSize; 
     currentBlock = newBlock; 
    } 

private: 
    Block* currentBlock; 
}; 

Để trả lời câu hỏi của bạn, bạn không gọi hành vi undefined vì không ai là sử dụng con trỏ cho đến khi đối tượng được xây dựng hoàn toàn (giá trị con trỏ chính nó là an toàn để sao chép xung quanh cho đến khi đó). Tuy nhiên, nó là một phương pháp khá xâm nhập, như là (các) đối tượng mà họ cần phải biết về nhóm bộ nhớ. Ngoài ra, nếu bạn đang xây dựng một số lượng lớn các đối tượng nhỏ, có khả năng sẽ nhanh hơn khi sử dụng một nhóm bộ nhớ thực (như hồ bơi của tôi) thay vì gọi đến new cho mọi đối tượng.

Dù phương pháp tiếp cận giống như hồ bơi bạn sử dụng, hãy cẩn thận rằng các đối tượng không bao giờ được chỉnh sửa theo cách thủ công delete, vì điều đó sẽ dẫn đến miễn phí gấp đôi!

9

Trong trường hợp bạn chưa làm quen, hãy tự làm quen với Boost.Pool. Từ tài liệu

Hồ bơi là gì?

Phân bổ hồ bơi là sơ đồ phân bổ bộ nhớ rất nhanh, nhưng bị giới hạn trong việc sử dụng. Để biết thêm thông tin về phân bổ hồ bơi (cũng là được gọi là lưu trữ riêng biệt đơn giản, hãy xem khái niệm khái niệm và Đơn giản Bộ nhớ được tách biệt).

Tại sao tôi nên sử dụng Bể bơi?

Sử dụng nhóm cung cấp cho bạn nhiều quyền kiểm soát hơn về cách sử dụng bộ nhớ trong chương trình của bạn. Ví dụ, bạn có thể có một tình huống mà bạn muốn phân bổ một loạt các đối tượng nhỏ tại một thời điểm, và sau đó đạt đến một điểm trong chương trình của bạn mà không cần thêm bất kỳ thứ gì trong số chúng.Sử dụng các giao diện của hồ bơi, bạn có thể chọn chạy các trình phá hủy của chúng hoặc chỉ cần thả chúng vào quên lãng; giao diện hồ bơi sẽ đảm bảo rằng không có rò rỉ bộ nhớ hệ thống .

Khi nào tôi nên sử dụng Bể bơi?

Hồ bơi thường được sử dụng khi có nhiều phân bổ và deallocation của các đối tượng nhỏ. Một cách sử dụng phổ biến khác là tình huống ở trên, nơi nhiều đối tượng có thể bị xóa khỏi bộ nhớ.

Nói chung, hãy sử dụng Bể bơi khi bạn cần một cách hiệu quả hơn để thực hiện điều khiển bộ nhớ bất thường .

Tôi nên sử dụng trình phân bổ hồ bơi nào?

pool_allocator là giải pháp đa năng hơn, hướng tới phục vụ hiệu quả các yêu cầu cho bất kỳ số lượng khối tiếp giáp nào.

fast_pool_allocator cũng là giải pháp có mục đích chung nhưng được hướng hướng tới các yêu cầu dịch vụ hiệu quả cho một đoạn tại một thời điểm; nó sẽ hoạt động cho các khối liền kề, nhưng không hoạt động như pool_allocator.

Nếu bạn đang lo ngại về hiệu suất, sử dụng fast_pool_allocator khi giao dịch với container như std::list, và sử dụng pool_allocator khi giao dịch với container như std::vector.

Quản lý bộ nhớ là kinh doanh khó khăn (luồng, bộ nhớ đệm, sắp xếp, phân mảnh, vv vv) Đối với mã sản xuất nghiêm trọng, được thiết kế tốt và các thư viện được tối ưu hóa một cách cẩn thận là con đường để đi, trừ khi hồ sơ của bạn cho thấy một nút cổ chai.

10

Ý tưởng của bạn tuyệt vời và hàng triệu ứng dụng đã sử dụng nó. Mẫu này nổi tiếng nhất là «hồ bơi tự động». Nó tạo thành một cơ sở để quản lý bộ nhớ “thông minh” trong các khung công tác Objective-C của Cocoa và Cocoa. Mặc dù thực tế là C + + cung cấp địa ngục của rất nhiều lựa chọn thay thế khác, tôi vẫn nghĩ rằng ý tưởng này có rất nhiều lộn ngược. Nhưng có một vài điều mà tôi nghĩ rằng việc triển khai của bạn vì nó có thể bị thiếu hụt.

Vấn đề đầu tiên mà tôi có thể nghĩ đến là an toàn luồng. Ví dụ, điều gì xảy ra khi các đối tượng của cùng một cơ sở được tạo ra từ các luồng khác nhau? Một giải pháp có thể là để bảo vệ quyền truy cập hồ bơi với các khóa loại trừ lẫn nhau. Mặc dù tôi nghĩ rằng một cách tốt hơn để làm điều này là để làm cho hồ bơi đó là một đối tượng cụ thể theo chủ đề.

Vấn đề thứ hai là gọi hành vi không xác định trong trường hợp hàm tạo của lớp dẫn xuất ném ra một ngoại lệ. Bạn thấy đấy, nếu điều đó xảy ra, đối tượng dẫn xuất sẽ không được xây dựng, nhưng hàm tạo của B của bạn sẽ đã đẩy con trỏ tới this tới vectơ. Sau đó, khi vectơ bị xóa, nó sẽ cố gắng gọi một destructor thông qua một bảng ảo của đối tượng không tồn tại hoặc thực tế là một đối tượng khác (vì new có thể tái sử dụng địa chỉ đó). Điều thứ ba tôi không thích là bạn chỉ có một hồ bơi toàn cầu, ngay cả khi nó chỉ là một chủ đề cụ thể, điều đó không cho phép kiểm soát tốt hơn đối với phạm vi của các đối tượng được phân bổ.

Lấy trên vào tài khoản, tôi sẽ làm một vài cải tiến:

  1. Có một chồng hồ để kiểm soát phạm vi nhiều hạt mịn.
  2. Làm cho nhóm đó xếp chồng một đối tượng theo chủ đề cụ thể.
  3. Trong trường hợp lỗi (như ngoại lệ trong hàm tạo lớp dẫn xuất), hãy đảm bảo rằng nhóm không giữ con trỏ đang lơ lửng.

Đây là nghĩa đen 5 phút tôi giải pháp, không đánh cho nhanh chóng và dơ bẩn:

#include <new> 
#include <set> 
#include <stack> 
#include <cassert> 
#include <memory> 
#include <stdexcept> 
#include <iostream> 

#define thread_local __thread // Sorry, my compiler doesn't C++11 thread locals 

struct AutoReleaseObject { 
    AutoReleaseObject(); 
    virtual ~AutoReleaseObject(); 
}; 

class AutoReleasePool final { 
    public: 
    AutoReleasePool() { 
     stack_.emplace(this); 
    } 

    ~AutoReleasePool() noexcept { 
     std::set<AutoReleaseObject *> obj; 
     obj.swap(objects_); 
     for (auto *p : obj) { 
      delete p; 
     } 
     stack_.pop(); 
    } 

    static AutoReleasePool &instance() { 
     assert(!stack_.empty()); 
     return *stack_.top(); 
    } 

    void add(AutoReleaseObject *obj) { 
     objects_.insert(obj); 
    } 

    void del(AutoReleaseObject *obj) { 
     objects_.erase(obj); 
    } 

    AutoReleasePool(const AutoReleasePool &) = delete; 
    AutoReleasePool &operator = (const AutoReleasePool &) = delete; 

    private: 
    // Hopefully, making this private won't allow users to create pool 
    // not on stack that easily... But it won't make it impossible of course. 
    void *operator new(size_t size) { 
     return ::operator new(size); 
    } 

    std::set<AutoReleaseObject *> objects_; 

    struct PrivateTraits {}; 

    AutoReleasePool(const PrivateTraits &) { 
    } 

    struct Stack final : std::stack<AutoReleasePool *> { 
     Stack() { 
      std::unique_ptr<AutoReleasePool> pool 
       (new AutoReleasePool(PrivateTraits())); 
      push(pool.get()); 
      pool.release(); 
     } 

     ~Stack() { 
      assert(!stack_.empty()); 
      delete stack_.top(); 
     } 
    }; 

    static thread_local Stack stack_; 
}; 

thread_local AutoReleasePool::Stack AutoReleasePool::stack_; 

AutoReleaseObject::AutoReleaseObject() 
{ 
    AutoReleasePool::instance().add(this); 
} 

AutoReleaseObject::~AutoReleaseObject() 
{ 
    AutoReleasePool::instance().del(this); 
} 

// Some usage example... 

struct MyObj : AutoReleaseObject { 
    MyObj() { 
     std::cout << "MyObj::MyObj(" << this << ")" << std::endl; 
    } 

    ~MyObj() override { 
     std::cout << "MyObj::~MyObj(" << this << ")" << std::endl; 
    } 

    void bar() { 
     std::cout << "MyObj::bar(" << this << ")" << std::endl; 
    } 
}; 

struct MyObjBad final : AutoReleaseObject { 
    MyObjBad() { 
     throw std::runtime_error("oops!"); 
    } 

    ~MyObjBad() override { 
    } 
}; 

void bar() 
{ 
    AutoReleasePool local_scope; 
    for (int i = 0; i < 3; ++i) { 
     auto o = new MyObj(); 
     o->bar(); 
    } 
} 

void foo() 
{ 
    for (int i = 0; i < 2; ++i) { 
     auto o = new MyObj(); 
     bar(); 
     o->bar(); 
    } 
} 

int main() 
{ 
    std::cout << "main start..." << std::endl; 
    foo(); 
    std::cout << "main end..." << std::endl; 
} 
2

Điều này nghe những gì tôi đã nghe được gọi là tuyến tính cấp phát. Tôi sẽ giải thích những điều cơ bản về cách tôi hiểu cách hoạt động của nó.

  1. Phân bổ khối bộ nhớ bằng cách sử dụng :: toán tử mới (kích thước);
  2. Có khoảng trống * là Con trỏ của bạn đến không gian trống tiếp theo trong bộ nhớ.
  3. Bạn sẽ có chức năng phân bổ (size_t size) sẽ cung cấp cho bạn con trỏ tới vị trí trong khối từ bước một để bạn có thể xây dựng để sử dụng Vị trí mới
  4. Vị trí mới trông giống như ... int * i = new (location) int(); nơi vị trí là khoảng trống * đối với một khối bộ nhớ mà bạn đã cấp phát từ bộ cấp phát.
  5. khi bạn làm xong tất cả bộ nhớ, bạn sẽ gọi hàm Flush() sẽ giải phóng bộ nhớ khỏi hồ bơi hoặc ít nhất là xóa sạch dữ liệu.

Tôi đã lập trình một trong những tính năng này gần đây và tôi sẽ đăng mã của tôi ở đây cho bạn cũng như làm hết sức mình để giải thích.

#include <iostream> 
    class LinearAllocator:public ObjectBase 
    { 
    public: 
     LinearAllocator(); 
     LinearAllocator(Pool* pool,size_t size); 
     ~LinearAllocator(); 
     void* Alloc(Size_t size); 
     void Flush(); 
    private: 
     void** m_pBlock; 
     void* m_pHeadFree; 
     void* m_pEnd; 
    }; 

đừng lo lắng về những gì tôi đang kế thừa. tôi đã sử dụng phân bổ này cùng với một nhóm bộ nhớ. nhưng về cơ bản thay vì nhận được bộ nhớ từ nhà điều hành mới, tôi đang nhận được bộ nhớ từ một bộ nhớ. các hoạt động nội bộ cũng giống nhau.

Đây là việc thực hiện:

LinearAllocator::LinearAllocator():ObjectBase::ObjectBase() 
{ 
    m_pBlock = nullptr; 
    m_pHeadFree = nullptr; 
    m_pEnd=nullptr; 
} 

LinearAllocator::LinearAllocator(Pool* pool,size_t size):ObjectBase::ObjectBase(pool) 
{ 
    if (pool!=nullptr) { 
     m_pBlock = ObjectBase::AllocFromPool(size); 
     m_pHeadFree = * m_pBlock; 
     m_pEnd = (void*)((unsigned char*)*m_pBlock+size); 
    } 
    else{ 
     m_pBlock = nullptr; 
     m_pHeadFree = nullptr; 
     m_pEnd=nullptr; 
    } 
} 
LinearAllocator::~LinearAllocator() 
{ 
    if (m_pBlock!=nullptr) { 
     ObjectBase::FreeFromPool(m_pBlock); 
    } 
    m_pBlock = nullptr; 
    m_pHeadFree = nullptr; 
    m_pEnd=nullptr; 
} 
MemoryBlock* LinearAllocator::Alloc(size_t size) 
{ 
    if (m_pBlock!=nullptr) { 
     void* test = (void*)((unsigned char*)m_pEnd-size); 
     if (m_pHeadFree<=test) { 
      void* temp = m_pHeadFree; 
      m_pHeadFree=(void*)((unsigned char*)m_pHeadFree+size); 
      return temp; 
     }else{ 
      return nullptr; 
     } 
    }else return nullptr; 
} 
void LinearAllocator::Flush() 
{ 
    if (m_pBlock!=nullptr) { 
     m_pHeadFree=m_pBlock; 
     size_t size = (unsigned char*)m_pEnd-(unsigned char*)*m_pBlock; 
     memset(*m_pBlock,0,size); 
    } 
} 

Mã này là đầy đủ chức năng, ngoại trừ một vài dòng mà sẽ cần phải được thay đổi bởi vì thừa kế và sử dụng hồ bơi bộ nhớ của tôi. nhưng tôi đặt cược bạn có thể tìm ra những gì cần phải thay đổi và chỉ cho tôi biết nếu bạn cần một bàn tay thay đổi mã. Mã này chưa được thử nghiệm trong bất kỳ loại trang viên chuyên nghiệp nào và không được bảo đảm là luồng an toàn hoặc bất kỳ thứ gì lạ mắt như thế. tôi chỉ đánh nó lên và nghĩ rằng tôi có thể chia sẻ nó với bạn vì bạn dường như cần giúp đỡ.

Tôi cũng có một triển khai hoạt động của một nhóm bộ nhớ hoàn toàn chung chung nếu bạn nghĩ rằng nó có thể giúp bạn. Tôi có thể giải thích nó hoạt động như thế nào nếu bạn cần.

Một lần nữa nếu bạn cần bất kỳ trợ giúp nào cho tôi biết. Chúc may mắn.

+1

Bước 3.5: bạn sẽ lo lắng về việc liệu hàm alloc() của bạn có xử lý đúng các vấn đề liên kết nếu nó được yêu cầu phân bổ không gian cho các kiểu đối tượng khác nhau hay không. –

3

tôi vẫn nghĩ rằng đây là một câu hỏi thú vị mà không có một câu trả lời dứt khoát, nhưng xin hãy để tôi chia nó ra thành những câu hỏi khác nhau mà bạn đang thực sự hỏi:

1.) Việc chèn một con trỏ vào một lớp cơ sở vào một vectơ trước khi khởi tạo một lớp con ngăn chặn hoặc gây ra các vấn đề với việc truy xuất các lớp kế thừa từ con trỏ đó. [Slicing ví dụ.]

Trả lời: Không, chừng nào bạn chắc chắn 100% của các loại có liên quan đang được chỉ ra, cơ chế này không gây ra tuy nhiên những vấn đề lưu ý những điểm sau đây:

Nếu constructor có nguồn gốc không thành công, bạn bị bỏ lại sau khi bạn có khả năng có một con trỏ lơ lửng ít nhất là ngồi trong vectơ, vì không gian địa chỉ đó [lớp dẫn xuất] nghĩ rằng nó đang được giải phóng cho môi trường hoạt động trên thất bại, nhưng vector vẫn có địa chỉ như là kiểu lớp cơ sở. Lưu ý rằng một vector, mặc dù loại hữu ích, không phải là cấu trúc tốt nhất cho điều này, và ngay cả khi nó được, cần có một số đảo ngược kiểm soát tham gia ở đây để cho phép các đối tượng vector để kiểm soát initialisation của các đối tượng của bạn, do đó, rằng bạn có nhận thức về thành công/thất bại.

Những điểm này dẫn đến câu hỏi thứ 2 ngụ ý:

2.) Đây có phải là một mô hình tốt cho tổng hợp?

Trả lời: Không thực sự, vì những lý do được đề cập ở trên, cộng với những lý do khác (Đẩy một vectơ qua điểm cuối của nó về cơ bản kết thúc bằng một malloc không cần thiết và sẽ ảnh hưởng đến hiệu suất.) Lý tưởng nhất là bạn muốn sử dụng thư viện tổng hợp, hoặc một lớp mẫu và thậm chí tốt hơn, tách riêng việc triển khai chính sách phân bổ/phân bổ khỏi việc triển khai pool, với một giải pháp mức thấp đã được gợi ý, để phân bổ bộ nhớ hồ bơi đầy đủ từ khởi tạo pool và sau đó sử dụng con trỏ này Sử dụng mẫu này, việc phá hủy hồ bơi là an toàn vì hồ bơi sẽ là bộ nhớ liền kề có thể bị phá hủy mà không có bất kỳ vấn đề lơ lửng nào, hoặc rò rỉ bộ nhớ thông qua việc mất tất cả các tham chiếu đối tượng (mất tất cả tham chiếu đến đối tượng có địa chỉ được cấp phát thông qua poo l bởi người quản lý lưu trữ lá bạn với chunk bẩn/s, nhưng sẽ không gây ra một rò rỉ bộ nhớ vì nó được quản lý bởi việc thực hiện hồ bơi.

Trong những ngày đầu của C/C++ (trước khi phổ biến khối lượng của STL), đây là một mô hình tốt đã thảo luận và việc triển khai nhiều và thiết kế có thể được tìm thấy trên mạng trong văn học tốt: Như một ví dụ:

Knuth (1973 Nghệ thuật lập trình máy tính: nhiều tập), và đối với một danh sách đầy đủ hơn, với thêm về tổng hợp, xem:

http://www.ibm.com/developerworks/library/l-memory/

câu hỏi ngụ ý thứ 3 có vẻ là:

3) Đây có phải là kịch bản hợp lệ để sử dụng tổng hợp không?

Trả lời: Đây là quyết định thiết kế được bản địa hóa dựa trên những gì bạn cảm thấy thoải mái, nhưng thành thật mà nói, việc thực hiện của bạn (không có cấu trúc kiểm soát/tổng hợp, có thể chia sẻ tập hợp các bộ đối tượng phụ) cho tôi biết tốt hơn với một danh sách liên kết cơ bản của các đối tượng bao bọc, mỗi đối tượng chứa một con trỏ đến lớp cha của bạn, chỉ được sử dụng cho mục đích giải quyết. Cấu trúc chu kỳ của bạn được xây dựng trên đầu trang này, và bạn chỉ cần sửa đổi/thu nhỏ danh sách theo yêu cầu để chứa tất cả các đối tượng lớp đầu tiên của bạn theo yêu cầu, và khi kết thúc, bạn có thể dễ dàng tiêu diệt chúng một cách hiệu quả một hoạt động O (1) từ trong danh sách được liên kết.

Có nói rằng, cá nhân tôi khuyên bạn nên tại thời điểm này (khi bạn có một kịch bản mà tổng hợp có sử dụng và vì vậy bạn đang ở trong tâm trí đúng) để thực hiện việc xây dựng một quản lý lưu trữ/tổng hợp thiết lập các lớp được paramaterised/typeless bây giờ vì nó sẽ giữ bạn trong tốt cho tương lai.