2008-12-29 26 views
7

Cách tốt nhất để có một mảng kết hợp với các loại giá trị tùy ý cho mỗi khóa trong C++ là gì?C++ mảng liên kết với các loại tùy ý cho các giá trị

Hiện tại, kế hoạch của tôi là tạo lớp "giá trị" với các biến thành viên thuộc loại tôi sẽ mong đợi. Ví dụ:

class Value { 

    int iValue; 
    Value(int v) { iValue = v; } 

    std::string sValue; 
    Value(std::string v) { sValue = v; } 

    SomeClass *cValue; 
    Value(SomeClass *v) { cValue = c; } 

}; 

std::map<std::string, Value> table; 

Nhược điểm với điều này là bạn phải biết loại khi truy cập "Giá trị". tức là:

table["something"] = Value(5); 
SomeClass *s = table["something"].cValue; // broken pointer 

Cũng có nhiều loại được đặt trong Giá trị, mảng càng cồng kềnh sẽ càng lớn.

Bất kỳ đề xuất nào tốt hơn?

+0

BTW, std :: bản đồ không phải là bảng băm; nó thường được thực hiện như một cây màu đỏ-đen, nhưng trong mọi trường hợp, các phím được giữ theo thứ tự. Có std :: tr1 :: unordered_map, thường được thực hiện như một bảng băm. –

+0

Chris, cảm ơn vì đã chỉ ra điều đó. Tôi bây giờ gọi nó là một "mảng kết hợp". –

+0

liên quan: ["điểm của mảng dị thể là gì?"] (Http://stackoverflow.com/questions/4534612/what-is-the-point-of-heterogenous-arrays) –

Trả lời

2

Phân lớp Value với IntValue, StringValue, v.v.

9

Phương pháp tiếp cận của bạn về cơ bản là đi đúng hướng. Bạn sẽ phải biết loại bạn đưa vào. Bạn có thể sử dụng boost::any và bạn sẽ có thể đặt bất cứ thứ gì vào bản đồ, miễn là bạn biết những gì bạn đưa vào:

std::map<std::string, boost::any> table; 
table["hello"] = 10; 
std::cout << boost::any_cast<int>(table["hello"]); // outputs 10 

Một số câu trả lời khuyến cáo việc sử dụng boost::variant để giải quyết vấn đề này. Nhưng nó sẽ không cho phép bạn lưu trữ các giá trị được đánh máy tùy ý trong bản đồ (như bạn muốn). Bạn phải biết tập hợp các loại có thể có trước đó. Cho rằng, bạn có thể làm các việc trên dễ dàng hơn:

typedef boost::variant<int, std::string, void*> variant_type; 
std::map<std::string, variant_type> table; 
table["hello"] = 10; 
// outputs 10. we don't have to know the type last assigned to the variant 
// but the variant keeps track of it internally. 
std::cout << table["hello"]; 

đó làm việc vì boost::variant quá tải operator<< cho mục đích đó. Điều quan trọng là phải hiểu rằng nếu bạn muốn lưu những gì hiện đang chứa trong biến thể, bạn vẫn phải biết loại, như với trong trường hợp boost::any:

typedef boost::variant<int, std::string, void*> variant_type; 
std::map<std::string, variant_type> table; 
table["hello"] = "bar"; 
std::string value = boost::get<std::string>(table["hello"]); 

Trình tự công việc để một biến thể là một thời gian chạy thuộc tính của luồng điều khiển của mã của bạn, nhưng loại được sử dụng của bất kỳ biến nào được xác định tại thời gian biên dịch. Vì vậy, nếu bạn muốn nhận được giá trị của biến thể, bạn phải biết loại của nó. Cách khác là sử dụng lượt truy cập, như được nêu trong tài liệu biến thể. Nó hoạt động bởi vì các biến thể lưu trữ một mã mà nói với nó mà loại cuối cùng được gán cho nó. Dựa trên đó, nó quyết định khi chạy sự quá tải của khách truy cập mà nó sử dụng. boost::variant là khá lớn và không hoàn toàn tuân thủ tiêu chuẩn, trong khi boost::any là tiêu chuẩn tuân thủ nhưng sử dụng bộ nhớ động ngay cả đối với các loại nhỏ (vì vậy nó chậm hơn. Biến thể có thể sử dụng ngăn xếp cho các loại nhỏ). Vì vậy, bạn phải trao đổi những gì bạn sử dụng.

Nếu bạn thực sự muốn đặt các đối tượng vào nó chỉ khác nhau theo cách họ làm điều gì đó, đa hình là một cách tốt hơn để đi. Bạn có thể có một cơ sở đẳng cấp mà bạn lấy được từ:

std::map< std::string, boost::shared_ptr<Base> > table; 
table["hello"] = boost::shared_ptr<Base>(new Apple(...)); 
table["hello"]->print(); 

Mà về cơ bản sẽ yêu cầu bố trí lớp này:

class Base { 
public: 
    virtual ~Base() { } 
    // derived classes implement this: 
    virtual void print() = 0; 
}; 

class Apple : public Base { 
public: 
    virtual void print() { 
     // print us out. 
    } 
}; 

Các boost::shared_ptr là một cái gọi là con trỏ thông minh. Nó sẽ tự động xóa các đối tượng của bạn nếu bạn xóa chúng ra khỏi bản đồ của bạn và không có gì khác là tham chiếu chúng với chúng nữa. Về lý thuyết, bạn có thể đã làm việc với một con trỏ đơn giản, nhưng sử dụng một con trỏ thông minh sẽ làm tăng đáng kể sự an toàn.Đọc hướng dẫn shared_ptr tôi liên kết đến.

+0

Đó là thời gian chạy kiểu-kiểm tra Tùy chọn. Đối với một tùy chọn kiểm tra kiểu thời gian biên dịch, cũng có boost :: variant (sử dụng với mẫu khách truy cập cho một giải pháp kiểm tra kiểu tĩnh kín). –

+0

tốt, chúng tôi đã nói về điều đó trong irc tôi nghĩ :) chỉ dành cho tất cả những người khác đọc nó: biến thể chỉ có thể lưu trữ một nhóm giới hạn các loại và nếu muốn có giá trị ngoài mục, người dùng có thể sử dụng lượt truy cập (và sẽ nhận được giá trị mà không biết loại của nó), nhưng không thể lưu giá trị ở bất kỳ đâu. –

+0

vì quyết định phiên bản nào của khách truy cập được gọi là được thực hiện khi chạy. loại biến, tuy nhiên, được xác định tại thời gian biên dịch. do đó bạn không thể thực hiện SomeAutoDeducedType result = boost :: apply_visitor (my_visitor(), u); . tất cả op() của khách truy cập đều có cùng loại trả về. –

2

Bạn có thể sử dụng liên minh với std :: map không?

Tăng :: biến thể cung cấp các biến không cần thiết.

Một cách tương tự, bạn có thể đặt tất cả thành viên Dữ liệu giá trị của mình ở chế độ riêng tư và cung cấp cho người truy cập trả lại lỗi (hoặc ném) nếu không được đặt.

+0

Bạn có thể sử dụng một liên kết với std :: map, nhưng các công đoàn chỉ có thể giữ các đối tượng kiểu POD. – mstrobl

1

Tối ưu hóa chuyển tiếp thẳng sẽ là sử dụng union, vì bạn sẽ luôn chỉ có một trong các giá trị dưới dạng khóa.

Một giải pháp hoàn chỉnh hơn sẽ gói gọn một số thông tin loại thời gian chạy vào giao diện. Chủ yếu "Loại này là gì?" và "Làm cách nào để so sánh các giá trị cho sự bình đẳng?" Sau đó, sử dụng các cài đặt đó làm khóa.

+0

Công đoàn không thể giữ tiêu chuẩn :: chuỗi – MSalters

+0

Thực tế, g ++ than phiền "lỗi: thành viên‘ std :: string [...] ’với hàm tạo không được phép trong liên minh" (trong số những thứ khác). C++ của tôi đang bị gỉ: - / –

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