2009-02-20 12 views
13

Tôi đang cố gắng để khai báo một và một lớp Column, với có một tin std::map với các giá trị trỏ đến một templated Column. Một cái gì đó như thế này:C++ std :: bản đồ của mẫu hạng đánh giá cao

template <typename T> 
class DataType { 
    private: 
    T type; 
}; 
template <typename T> 
class Field { 
    private: 
    T value; 
    DataType<T> value; 
}; 
class Row { 
    private: 
    std::map<unsigned long,Field*> column; 
}; 

Vâng, tôi cho rằng về nguyên tắc lớp không cần phải biết được loại Field (hoặc Column), chúng tôi muốn sử dụng, tức là cho dù đó là một Field<int> trong cột 1 hoặc a Field<double> trong cột 2. Nhưng tôi không chắc cú pháp chính xác của khai báo Row::column là gì, hoặc nếu std::map bị giới hạn theo nghĩa này và tôi nên sử dụng cái gì khác.

Tôi gửi cho bạn các đề xuất và cảm ơn bạn đã đề xuất trước.

+0

Vậy câu hỏi là gì? –

+1

bạn không phải chuyển mã của mình thành html. chỉ cần đặt nó như là, với 4 chars thụt đầu dòng. –

+0

tới Dave: Câu hỏi của tôi là: Vì Trường được tạo khuôn mẫu, làm cách nào tôi có thể "cho biết" sơ đồ :: bản đồ mà các giá trị là "bất kỳ loại Trường nào"? để litb: Cảm ơn bạn đã đề xuất! :-) – jbatista

Trả lời

22

Field một mình không phải là một loại, nhưng mẫu có thể tạo một nhóm các loại, chẳng hạn như Field<int>Field<double>. Tất cả các trường này không liên quan sao cho một trường nào đó có nguồn gốc từ cái kia hay cái kia. Vì vậy, bạn phải thiết lập một số mối quan hệ giữa tất cả các loại được tạo ra. Một cách là sử dụng một lớp cơ sở không phải mẫu chung:

class FieldBase { }; 

template <typename T> 
class Field : public FieldBase { 
    private: 
    T value; 
    DataType<T> type; 
}; 
class Row { 
    private: 
    std::map<unsigned long,FieldBase*> column; 
}; 

Và xem xét sử dụng con trỏ thông minh thay vì con trỏ thô trong mã. Dù sao, bây giờ vấn đề là loại thông tin bị mất - cho dù bạn trỏ đến một số Field<double> hoặc đến một số Field<int> không còn được biết nữa và chỉ có thể được phát hiện bằng cách giữ một số loại cờ trong cơ sở được đặt bởi lớp dẫn xuất - hoặc bằng cách yêu cầu RTTI sử dụng

dynamic_cast<Field<int>*>(field) != 0 

Nhưng điều đó thật xấu xí. Đặc biệt là vì những gì bạn muốn có một ngữ nghĩa giá trị. Tôi muốn bạn có thể sao chép hàng của bạn và nó sẽ sao chép tất cả các trường trong đó. Và bạn sẽ muốn có được một đôi khi một đôi được lưu trữ - mà không cần đầu tiên sử dụng RTTI để hack theo cách của bạn đến loại có nguồn gốc.

Một cách để thực hiện việc này là sử dụng một công đoàn bị phân biệt đối xử. Về cơ bản, đó là một liên minh cho một số loại tùy ý và ngoài ra một loại cờ, lưu trữ giá trị nào hiện được lưu trữ trong trường đó (ví dụ: double, int, ...). Ví dụ:

template <typename T> 
class Field { 
    private: 
    T value; 
    DataType<T> type; 
}; 
class Row { 
    private: 
    std::map<unsigned long, 
      boost::variant< Field<int>, Field<double> > > 
     column; 
}; 

boost :: variant làm tất cả công việc cho bạn. Bạn có thể sử dụng truy cập để làm cho nó gọi một functor bằng cách sử dụng quyền quá tải.Hãy xem qua số manual

1
  1. Bạn gặp lỗi ở đó: bạn phải "thành viên" có giá trị trong trường (có thể là "loại").
  2. Vui lòng không giữ nguyên con trỏ trong giá trị của bản đồ. Sử dụng boost::shared_ptr.
  3. Ngoài ra, bạn nên có lý do chính đáng để viết các lớp như vậy khi có rất nhiều mã xử lý DB/bảng đã có sẵn mà bạn có thể sử dụng. Vì vậy, nếu nó được áp dụng, hãy xem xét sử dụng một cái gì đó hiện có và không viết mã xử lý bảng của riêng bạn.

Bây giờ, để trả lời câu hỏi của bạn :), trường <> các lớp có thể kế thừa từ một lớp cơ sở chung được chia sẻ bởi tất cả các loại dữ liệu. Bằng cách này một container như bản đồ cột của bạn có thể giữ con trỏ (làm cho rằng được chia sẻ con trỏ) cho các đối tượng có nguồn gốc được instanced của một lớp mẫu.

+0

1. Xin lỗi bạn đã đúng, ý tôi là: Đại lộ T; DataType atype; 2. Tôi không quen thuộc với thư viện Boost (tôi cho rằng đó là những gì bạn đang nói về), nhưng tôi sẽ nhìn vào nó, cảm ơn. 3. Bạn có thể vui lòng chỉ một hoặc hai đề xuất mà tôi có thể xem xét? – jbatista

0

A Row< int, float, int> khác với Row<int, std::string>. Rõ ràng, Row<int,float,int>.field<0> phải là Field<int> trong khi Row<int,float,int>.field<1> phải là Field<float>. Và Row<int,float,int>.field<3> là lỗi trình biên dịch.

Cách dễ nhất để làm như vậy là sử dụng Boost. Toàn bộ trí thông minh được tiên phong bởi Loki (xem Modern C++ Design, by Andrei Alexandrescu) nhưng Boost hiện đại hơn và được hỗ trợ tốt hơn.

Thông thường, bạn sẽ không lặp qua các trường - mỗi trường có loại riêng. Nhưng bạn làm, bạn thực sự cần một FieldBase. Nếu bạn cần một giao diện như vậy, có thể đáng giá để lưu trữ các trường nội bộ dưới dạng boost::array<FieldBase, N> (ví dụ: Row<int,float,int>boost::array<FieldBase, 3>). Tuy nhiên, bạn không bao giờ cần phải dynamic_cast rằng FieldBase*. Đó là thử nghiệm thời gian chạy và bạn luôn biết chính xác T của mỗi Field<T> lúc biên dịch.