2010-09-10 26 views
6

Đây là một phần tiếp theo của một related post mà hỏi những câu hỏi vĩnh cửu:Tôi có thể có các thùng chứa đa hình với ngữ nghĩa giá trị trong C++ 11 không?

Tôi có thể có container đa hình với ngữ nghĩa giá trị trong C++?

Câu hỏi đã được hỏi một cách không chính xác. Nó cần phải có được nhiều hơn như:

Tôi có thể đã STL container của một loại cơ sở lưu trữ bởi giá trị, trong đó các yếu tố thể hiện hành vi đa hình?

Nếu bạn đặt câu hỏi về C++, câu trả lời là "không". Tại một số điểm, bạn sẽ cắt các đối tượng được lưu trữ theo giá trị.

Bây giờ tôi hỏi lại câu hỏi, nhưng nghiêm chỉnh về C++ 11. Với những thay đổi đối với ngôn ngữ và các thư viện chuẩn, bây giờ có thể lưu trữ các đối tượng đa hình theo giá trị trong một container STL không?

Tôi biết rõ khả năng lưu trữ con trỏ thông minh vào lớp cơ sở trong vùng chứa - đây không phải là những gì tôi đang tìm kiếm, vì tôi đang cố gắng tạo đối tượng trên ngăn xếp mà không cần sử dụng new.

Cân nhắc nếu bạn sẽ (từ bài được liên kết) như cơ bản C++ ví dụ:

#include <iostream> 

using namespace std; 

class Parent 
{ 
    public: 
     Parent() : parent_mem(1) {} 
     virtual void write() { cout << "Parent: " << parent_mem << endl; } 
     int parent_mem; 
}; 

class Child : public Parent 
{ 
    public: 
     Child() : child_mem(2) { parent_mem = 2; } 
     void write() { cout << "Child: " << parent_mem << ", " << child_mem << endl; } 

     int child_mem; 
}; 

int main(int, char**) 
{ 
    // I can have a polymorphic container with pointer semantics 
    vector<Parent*> pointerVec; 

    pointerVec.push_back(new Parent()); 
    pointerVec.push_back(new Child()); 

    pointerVec[0]->write();  
    pointerVec[1]->write();  

    // Output: 
    // 
    // Parent: 1 
    // Child: 2, 2 

    // But I can't do it with value semantics 

    vector<Parent> valueVec; 

    valueVec.push_back(Parent()); 
    valueVec.push_back(Child());  // gets turned into a Parent object :(

    valueVec[0].write();   
    valueVec[1].write();   

    // Output: 
    // 
    // Parent: 1 
    // Parent: 2 

} 
+2

tăng: ptr_vector phải làm những gì bạn muốn. –

+1

Lưu ý rằng bạn có thể mô phỏng ngữ nghĩa giá trị (ít nhất là về các container STL) với một cái gì đó như 'boost :: ptr_container'. –

+0

@Martin: Sao bạn dám đánh bại tôi trong 15 giây?!? –

Trả lời

4

Chỉ để cho vui, dựa trên nhận xét của James về một hệ thống dựa trên mẫu, tôi đã đưa ra cách triển khai giống như Vector này. Nó thiếu rất nhiều tính năng, và có thể là lỗi, nhưng đó là một sự khởi đầu!

#include <iostream> 
#include <vector> 
#include <boost/shared_ptr.hpp> 

template <typename T> 
class Vector 
{ 
public: 
    T &operator[] (int i) const { return p[i]->get(); } 
    template <typename D> 
    void push_back(D &x) { p.push_back(ptr_t(new DerivedNode<D>(x))); } 

private: 
    class Node 
    { 
    public: 
     virtual T &get() = 0; 
    }; 

    template <typename D> 
    class DerivedNode : public Node 
    { 
    public: 
     DerivedNode(D &x) : x(x) {} 
     virtual D &get() { return x; } 
    private: 
     D x; 
    }; 

    typedef boost::shared_ptr<Node> ptr_t; 
    std::vector<ptr_t> p; 
}; 

/////////////////////////////////////// 

class Parent 
{ 
    public: 
     Parent() : parent_mem(1) {} 
     virtual void write() const { std::cout << "Parent: " << parent_mem << std::endl; } 
     int parent_mem; 
}; 

class Child : public Parent 
{ 
    public: 
     Child() : child_mem(2) { parent_mem = 2; } 
     void write() const { std::cout << "Child: " << parent_mem << ", " << child_mem << std::endl; } 

     int child_mem; 
}; 


int main() 
{ 
    Vector<Parent> v; 

    v.push_back(Parent()); 
    v.push_back(Child()); 

    v[0].write(); 
    v[1].write(); 
} 
+0

Điều này gần với Thư viện nguồn Adobe 'nhiều 'các lớp. Tốt đẹp! – fbrereto

+2

bạn đang phát minh lại bánh xe: 'boost :: ptr_vector' làm tốt hơn (không có' shared_ptr' liên quan). Cũng lưu ý rằng yêu cầu cơ bản không có 'mới'. Với một 'mới' nó tầm thường (và có các thư viện). –

8

Bạn chắc chắn không thể có một mảng đa hình (hoặc vector). Yêu cầu rằng các phần tử của một mảng được lưu trữ liên tục trong bộ nhớ về cơ bản là không tương thích với thực tế là các loại lớp dẫn xuất khác nhau có thể có các kích cỡ khác nhau.

Không có thùng chứa thư viện chuẩn nào cho phép lưu trữ các đối tượng thuộc các loại lớp dẫn xuất khác nhau trong một vùng chứa duy nhất.

+0

Ngay cả với ví dụ: 'list', vấn đề về kích thước vẫn tồn tại: nút lớn đến mức nào? –

+1

@Oli: Vâng, suy nghĩ đầu tiên của tôi là bạn có thể có một mẫu lớp nút có nguồn gốc có thể được khởi tạo với các đối tượng có kích thước khác nhau, yêu cầu ít nhất (a) một cách tiêu chuẩn để xác định kích thước của loại động một đối tượng và (b) một cách tiêu chuẩn để sao chép một đối tượng. Mặc dù vậy, mặc dù, tôi nghĩ rằng sẽ có vấn đề với việc nhận được công văn động để hoạt động chính xác. Tôi không nghĩ rằng một container như vậy sẽ thực hiện bất kỳ tốt hơn so với một container của con trỏ, một trong hai. Tốt nhất là sẽ lộn xộn; nó hoàn toàn không thể. Nó sẽ là một dự án thú vị để chơi với, mặc dù. –

+0

xem câu trả lời của tôi! Tôi đã lấy ý tưởng của bạn và chạy với nó ... bình luận chào đón. –

2

Trước hết, yêu cầu của bạn vẫn chưa rõ ràng. Tôi sẽ giả định rằng bạn muốn "lưu trữ nội tuyến" cho vùng chứa; vì vậy, ví dụ, trong một "đa hình" vector, tất cả các phần tử sẽ nằm liền kề trong bộ nhớ (chỉ với phần đệm ở giữa khi cần để căn chỉnh chính xác).

Bây giờ, có thể nếu bạn sẵn sàng cung cấp danh sách đầy đủ tất cả các loại mà bạn sẽ đưa vào thùng chứa lúc biên dịch. Việc thực hiện đơn giản nhất là sử dụng một liên minh của tất cả các loại có thể là loại mảng sao lưu - đảm bảo đủ kích thước và căn chỉnh phù hợp, và cùng truy cập O (1) theo chỉ mục, với chi phí của một số không gian lãng phí trên các phần tử các loại kích thước nhỏ hơn. Tôi có thể đi vào điều này với nhiều chi tiết hơn nếu bạn muốn.

Nếu danh sách các loại được biết trước hoặc nếu bạn không muốn loại phí trên đó, bạn phải duy trì chỉ mục riêng biệt của con trỏ (hoặc bù từ đầu cửa hàng sao lưu) đến các yếu tố, để bạn có thể thực hiện truy cập O (1). Ngoài ra, cho các vấn đề liên kết, tôi không chắc chắn nếu bạn thậm chí có thể làm điều đó trong hoàn toàn di động C++ 03, mặc dù bạn chắc chắn có thể trong C + + 0x.

+0

Nếu các kiểu của bạn có các trình tạo bản sao, vv, thì bạn không thể đặt chúng thành một liên minh. –

+2

True, mặc dù bạn luôn có thể sử dụng cùng một hack mà 'boost :: variant' sử dụng (hoặc, cho rằng vấn đề, chỉ cần sử dụng nó trực tiếp). –

+1

+1: 'std :: vector >' là câu trả lời hay nhất mà tôi có thể đưa ra. Lưu ý rằng các khách truy cập của cấu trúc này chỉ cần 'toán tử return_type() (Base const &)' vì những người khác có thể chuyển đổi thành 'Base'. –

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