2008-10-03 25 views
6

Dường như tôi đã có một sự hiểu lầm cơ bản về C++: <C++ thay thế cho void * con trỏ (mà không phải là mẫu)

Tôi thích giải pháp chứa đa hình. Cảm ơn bạn SO, vì đã chú ý đến tôi :)


Vì vậy, chúng tôi cần tạo ra một đối tượng kiểu container tương đối chung. Nó cũng xảy ra để đóng gói một số logic liên quan đến kinh doanh. Tuy nhiên, chúng ta cần lưu trữ dữ liệu tùy ý về cơ bản trong vùng chứa này - mọi thứ từ các kiểu dữ liệu nguyên thủy đến các lớp phức tạp.

Do đó, người ta sẽ ngay lập tức chuyển sang ý tưởng về một lớp mẫu và được thực hiện với nó. Tuy nhiên, tôi đã nhận thấy đa hình C++ và các mẫu không chơi tốt với nhau. Là có một số logic phức tạp mà chúng ta sẽ phải làm việc, tôi chỉ muốn gắn bó với một trong hai mẫu HOẶC đa hình, và không cố gắng để chống lại C++ bằng cách làm cho nó làm cả hai.

Cuối cùng, với điều kiện tôi muốn làm cái này hay cái kia, tôi thích sự đa hình hơn. Tôi tìm thấy nó dễ dàng hơn để đại diện cho các ràng buộc như "container này chứa các loại so sánh" - a la java.

Đưa tôi đến chủ đề câu hỏi: Theo trừu tượng nhất, tôi tưởng tượng rằng tôi có thể có giao diện ảo thuần túy "Container" có thứ gì đó giống như "đẩy (void * dữ liệu) và pop (void * data)" (đối với hồ sơ, tôi không thực sự cố gắng để thực hiện một ngăn xếp).

Tuy nhiên, tôi không thực sự thích void * ở cấp cao nhất, chưa kể chữ ký sẽ thay đổi mỗi lần tôi muốn thêm ràng buộc vào loại dữ liệu mà vùng chứa cụ thể có thể làm việc.

Tóm tắt: Chúng tôi có các vùng chứa tương đối phức tạp có nhiều cách khác nhau để truy xuất các phần tử. Chúng tôi muốn có thể thay đổi các ràng buộc trên các yếu tố có thể đi vào các thùng chứa. Các phần tử nên làm việc với nhiều loại vùng chứa (miễn là chúng đáp ứng các ràng buộc của vùng chứa cụ thể đó).

Chỉnh sửa: Tôi cũng nên đề cập rằng các hộp chứa phải được đa hình. Đó là lý do chính của tôi vì không muốn sử dụng templated C++.

Vì vậy - tôi có nên bỏ tình yêu của mình đối với giao diện kiểu Java và đi kèm với mẫu không? Tôi có nên sử dụng void * và tĩnh đúc tất cả mọi thứ? Hoặc tôi nên đi với một định nghĩa lớp trống "Element" tuyên bố không có gì và sử dụng nó như là lớp cấp cao nhất của tôi trong hệ thống phân cấp "Element"?

Một trong những lý do khiến tôi yêu tràn ngăn xếp là nhiều phản hồi cung cấp một số thông tin chi tiết thú vị về các cách tiếp cận khác mà tôi chưa từng cân nhắc. Vì vậy, cảm ơn bạn trước cho thông tin chi tiết và nhận xét của bạn.

+0

ý của bạn là "đa hình và mẫu không chơi tốt với nhau"? –

+0

Cụ thể, một thùng chứa đa hình - Tôi quên đề cập đến yêu cầu đó. Theo như tôi biết, nó không thể được thực hiện .. nhưng sau đó, những gì tôi biết. – Voltaire

+0

> Tôi có nhu cầu về các thùng chứa của mình để được đa hình. Cập nhật câu trả lời của tôi cho phù hợp. – Lev

Trả lời

3

Bạn có thể không có một lớp container gốc có chứa các yếu tố:

template <typename T> 
class Container 
{ 
public: 

    // You'll likely want to use shared_ptr<T> instead. 
    virtual void push(T *element) = 0; 
    virtual T *pop() = 0; 
    virtual void InvokeSomeMethodOnAllItems() = 0; 
}; 

template <typename T> 
class List : public Container<T> 
{ 
    iterator begin(); 
    iterator end(); 
public: 
    virtual void push(T *element) {...} 
    virtual T* pop() { ... } 
    virtual void InvokeSomeMethodOnAllItems() 
    { 
     for(iterator currItem = begin(); currItem != end(); ++currItem) 
     { 
      T* item = *currItem; 
      item->SomeMethod(); 
     } 
    } 
}; 

Những container sau đó có thể được thông qua xung quanh polymorphically:

class Item 
{ 
public: 
    virtual void SomeMethod() = 0; 
}; 

class ConcreteItem 
{ 
public: 
    virtual void SomeMethod() 
    { 
     // Do something 
    } 
}; 

void AddItemToContainer(Container<Item> &container, Item *item) 
{ 
    container.push(item); 
} 

... 

List<Item> listInstance; 
AddItemToContainer(listInstance, new ConcreteItem()); 
listInstance.InvokeSomeMethodOnAllItems(); 

này cung cấp cho bạn giao diện container trong một loại an toàn cách chung chung.

Nếu bạn muốn thêm trở ngại đối với các loại của các yếu tố có thể được chứa, bạn có thể làm một cái gì đó như thế này:

class Item 
{ 
public: 
    virtual void SomeMethod() = 0; 
    typedef int CanBeContainedInList; 
}; 

template <typename T> 
class List : public Container<T> 
{ 
    typedef typename T::CanBeContainedInList ListGuard; 
    // ... as before 
}; 
+0

Đó là những gì tôi đang nghĩ. Vấn đề duy nhất là Item về bản chất là một lớp trống. Theo như tôi có thể nói, tôi có thể đặt không có giới hạn về những gì cụ thể được lưu trữ trong container, do đó có thực sự không phải là một giao diện chung tôi có thể giải nén. Nhưng có lẽ điều đó không quá tệ. – Voltaire

+0

Ngoài ra .. Tôi không nghĩ rằng bạn có thể có các chức năng ảo trong mẫu. Một vấn đề lớn khác;) – Voltaire

+0

Bạn có thể có các chức năng ảo trong mẫu. – Lev

5

Tính đa hình và các mẫu thực hiện rất tốt với nhau, nếu bạn sử dụng chúng đúng cách.

Dù sao, tôi hiểu rằng bạn chỉ muốn lưu trữ một loại đối tượng trong mỗi trường hợp vùng chứa. Nếu có, hãy sử dụng mẫu. Điều này sẽ ngăn bạn lưu nhầm loại đối tượng sai.

Đối với giao diện vùng chứa: Tùy thuộc vào thiết kế của bạn, có thể bạn cũng sẽ có thể tạo mẫu cho chúng và sau đó chúng sẽ có các phương thức như void push(T* new_element). Hãy suy nghĩ về những gì bạn sẽ biết về đối tượng khi bạn muốn thêm nó vào một thùng chứa (của một loại không xác định). Đối tượng sẽ đến từ đâu ngay từ đầu? Một hàm trả về void*? Bạn có biết rằng nó sẽ được so sánh?Ít nhất, nếu tất cả các lớp đối tượng được lưu trữ được xác định trong mã của bạn, bạn có thể làm cho tất cả chúng được kế thừa từ một tổ tiên chung, ví dụ: Storable và sử dụng Storable* thay vì void*.

Bây giờ, nếu bạn thấy các đối tượng đó sẽ luôn được thêm vào vùng chứa theo phương thức như void push(Storable* new_element), thì thực sự sẽ không có giá trị gia tăng nào khi tạo mẫu chứa. Nhưng sau đó bạn sẽ biết nó nên lưu trữ Storables.

+0

Mặc dù điều này có thể sai - tôi nghĩ rằng nó là an toàn để giả định rằng có, mỗi container sẽ lưu trữ một phần tử của một loại. Tuy nhiên, tôi có nhu cầu về các thùng chứa để chúng được đa hình. Đó là những gì tôi thấy khó khăn về các mẫu. – Voltaire

+0

"nếu bạn sử dụng chúng một cách chính xác" = sự sụp đổ của nhiều ý tưởng bị cáo buộc tốt. – DarenW

5

Điều đơn giản là xác định lớp cơ sở trừu tượng có tên là Container và phân lớp nó cho từng loại mục bạn có thể muốn lưu trữ. Sau đó, bạn có thể sử dụng bất kỳ lớp thu thập tiêu chuẩn nào (std::vector, std::list, v.v.) để lưu trữ con trỏ đến Container. Hãy nhớ rằng, vì bạn sẽ được lưu trữ con trỏ, bạn sẽ phải xử lý phân bổ/deallocation của họ.

Tuy nhiên, thực tế là bạn cần một bộ sưu tập duy nhất để lưu trữ các đối tượng của các loại cực kỳ khác nhau này là một dấu hiệu cho thấy có điều gì đó sai với thiết kế ứng dụng của bạn. Có thể nên xem xét lại logic nghiệp vụ trước khi bạn triển khai vùng chứa siêu chung này.

13

Bạn có thể xem sử dụng vùng chứa tiêu chuẩn boost::any nếu bạn đang lưu trữ dữ liệu thực sự tùy ý vào vùng chứa.

Nghe có vẻ giống như bạn thà có một cái gì đó giống như một boost::ptr_container nơi bất cứ điều gì mà thể được lưu trữ trong các thùng chứa phải xuất phát từ một số loại cơ sở, và hộp đựng chỉ có thể cung cấp cho bạn tham khảo của các loại cơ sở.

1

Sử dụng đa hình, về cơ bản bạn còn lại với một lớp cơ sở cho vùng chứa và các lớp dẫn xuất cho các kiểu dữ liệu. Lớp cơ sở/các lớp dẫn xuất có thể có nhiều hàm ảo như bạn cần, theo cả hai hướng.

Tất nhiên, điều này có nghĩa là bạn sẽ cần phải bao bọc các kiểu dữ liệu nguyên thủy trong các lớp dẫn xuất. Nếu bạn sẽ xem xét lại việc sử dụng các mẫu tổng thể, đây là nơi tôi sẽ sử dụng các mẫu. Tạo một lớp dẫn xuất từ ​​cơ sở là một khuôn mẫu và sử dụng nó cho các kiểu dữ liệu nguyên thủy (và các kiểu khác mà bạn không cần thêm bất kỳ chức năng nào hơn là mẫu được cung cấp).

Đừng quên rằng bạn có thể làm cho cuộc sống của bạn dễ dàng hơn bởi typedefs cho mỗi loại templated - đặc biệt là nếu sau này bạn cần phải biến một trong số họ vào một lớp.

3

Trước hết, các mẫu và đa hình là các khái niệm trực giao và chúng hoạt động tốt với nhau. Tiếp theo, tại sao bạn muốn có một cấu trúc dữ liệu cụ thể? Điều gì về STL hoặc tăng cấu trúc dữ liệu (cụ thể là pointer containter) không hoạt động cho bạn.

Với câu hỏi của bạn, có vẻ như bạn sẽ lạm dụng quyền thừa kế trong trường hợp của bạn. Bạn có thể tạo "constraints" về những gì có trong vùng chứa của bạn, đặc biệt nếu bạn đang sử dụng mẫu. Những hạn chế có thể vượt xa những gì trình biên dịch và trình liên kết của bạn sẽ cung cấp cho bạn. Nó thực sự là vụng về với loại điều đó với thừa kế và lỗi có nhiều khả năng còn lại cho thời gian chạy.

+1

Bản thân vùng chứa có các thuộc tính thú vị độc lập với dữ liệu mà nó lưu trữ. Và nó sẽ là tốt đẹp nếu nó là đa hình. Có thể có một thư viện nguồn mở gần với những gì chúng ta cần, nhưng tôi nghi ngờ nó phù hợp với yêu cầu của chúng ta. – Voltaire

1

Bạn cũng có thể muốn kiểm tra The Boost Concept Check Library (BCCL) được thiết kế để cung cấp những hạn chế trên các tham số mẫu của các lớp templated, các thùng chứa của bạn trong trường hợp này.

Và chỉ để nhắc lại những gì người khác đã nói, tôi chưa bao giờ gặp vấn đề khi trộn đa hình và mẫu, và tôi đã thực hiện một số nội dung khá phức tạp với chúng.

0

Bạn không thể phải từ bỏ giao diện giống như Java và sử dụng mẫu. Josh's suggestion của một khuôn mẫu cơ bản chung Container chắc chắn sẽ cho phép bạn làm đa hình vượt qua Container và con cái của họ xung quanh, nhưng ngoài ra bạn chắc chắn có thể thực hiện các giao diện như các lớp trừu tượng để được các mục chứa. Không có lý do bạn không thể tạo ra một lớp IComparable trừu tượng như bạn đề nghị, như vậy bạn có thể có một chức năng đa hình như sau:

class Whatever 
{ 
    void MyPolymorphicMethod(Container<IComparable*> &listOfComparables); 
} 

nay phương pháp này có thể mất bất cứ con của container có chứa bất kỳ lớp thực hiện IComparable, vì vậy nó sẽ cực kỳ linh hoạt.

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