7

看来我对 C++ 有一个根本的误解:<

我喜欢多态容器解决方案。谢谢你,让我注意到这一点:)


所以,我们需要创建一个相对通用的容器类型对象。它也恰好封装了一些业务相关的逻辑。然而,我们需要在这个容器中存储基本上任意的数据——从原始数据类型到复杂类的所有内容。

因此,人们会立即跳到模板类的想法并完成它。但是,我注意到 C++ 多态性和模板不能很好地协同工作。由于有一些复杂的逻辑我们将不得不工作,我宁愿只使用模板或多态性,而不是试图通过使其两者兼而有之来对抗 C++。

最后,鉴于我想做一个或另一个,我更喜欢多态性。我发现表示“这个容器包含 Comparable 类型”之类的约束要容易得多 - 一个 la java.lang.

把我带到问题的话题:最抽象的,我想我可以有一个“容器”纯虚拟接口,类似于“push(void * data)和pop(void * data)”(记录在案) ,我实际上并没有尝试实现堆栈)。

但是,我真的不喜欢顶层的 void*,更不用说每次我想为具体容器可以使用的数据类型添加约束时签名都会发生变化。

总结:我们有相对复杂的容器,有各种方法来检索元素。我们希望能够改变对可以进入容器的元素的约束。元素应该与多种容器一起使用(只要它们满足特定容器的约束)。

编辑:我还应该提到容器本身需要是多态的。这是我不想使用模板化 C++ 的主要原因。

那么 - 我应该放弃对 Java 类型接口的热爱并使用模板吗?我应该使用 void* 并静态转换所有内容吗?或者我应该使用一个空的类定义“元素”,它什么都不声明,并将其用作“元素”层次结构中的顶级类?

我喜欢堆栈溢出的原因之一是,许多回复提供了一些我从未考虑过的其他方法的有趣见解。因此,提前感谢您的见解和评论。

4

8 回答 8

14

如果您将真正的任意数据存储到容器中,您可以考虑使用boost::any的标准容器。

听起来您更像是更喜欢boost::ptr_container 之类的东西,其中可以存储在容器中的任何东西都必须从某些基本类型派生,而容器本身只能为您提供对基本类型的引用。

于 2008-10-03T19:21:11.790 回答
5

如果您正确使用它们,多态性和模板确实可以很好地结合使用。

无论如何,我知道您只想在每个容器实例中存储一种类型的对象。如果是这样,请使用模板。这将防止您错误地存储错误的对象类型。

至于容器接口:根据您的设计,也许您也可以将它们模板化,然后它们将具有类似void push(T* new_element). 想一想当您想将对象添加到(未知类型的)容器时您会知道什么。对象首先来自哪里?一个返回的函数void*?你知道它会是可比的吗?至少,如果在您的代码中定义了所有存储的对象类,您可以使它们都继承自一个共同的祖先,例如Storable,并使用Storable*而不是void*.

现在,如果您看到对象总是会通过类似的方法添加到容器中void push(Storable* new_element),那么将容器设置为模板确实没有任何附加值。但是你会知道它应该存储 Storables。

于 2008-10-03T19:16:49.773 回答
5

简单的事情是定义一个名为 的抽象基类Container,并为您可能希望存储的每种项目对其进行子类化。然后,您可以使用任何标准集合类(std::vectorstd::list等)来存储指向Container. 请记住,由于您将存储指针,因此您必须处理它们的分配/释放。

但是,您需要一个集合来存储如此不同类型的对象这一事实表明您的应用程序设计可能存在问题。在实现这个超通用容器之前重新审视业务逻辑可能会更好。

于 2008-10-03T19:18:36.010 回答
3

首先,模板和多态性是正交的概念,它们可以很好地协同工作。接下来,为什么要特定的数据结构?STL 或 boost 数据结构(特别是指针容器)对您不起作用。

鉴于您的问题,听起来您会在您的情况下滥用继承。可以对容器中的内容创建“约束”,尤其是在使用模板时。这些约束可能超出编译器和链接器所能提供的范围。实际上,对于继承和错误更有可能留在运行时的那种事情更尴尬。

于 2008-10-03T19:21:23.133 回答
3

你不能有一个包含元素的根容器类:

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();
       }
    }
};

然后可以多态地传递这些容器:

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();

这以类型安全的通用方式为您提供了 Container 接口。

如果要对可以包含的元素类型添加约束,可以执行以下操作:

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
};
于 2008-10-03T19:31:25.093 回答
1

使用多态性,您基本上只剩下容器的基类和数据类型的派生类。基类/派生类可以在两个方向上拥有任意数量的虚函数。

当然,这意味着您还需要将原始数据类型包装在派生类中。如果您要重新考虑整体模板的使用,这就是我将使用模板的地方。从作为模板的基类创建一个派生类,并将其用于原始数据类型(以及其他不需要模板提供的功能的其他类)。

不要忘记,您可能会通过 typedef 为每个模板化类型简化您的生活——尤其是当您以后需要将其中一个转换为类时。

于 2008-10-03T19:21:12.197 回答
1

您可能还想查看Boost 概念检查库 (BCCL),它旨在为模板化类的模板参数提供约束,在这种情况下是您的容器。

重申一下其他人所说的话,我从来没有遇到过混合多态性和模板的问题,而且我已经用它们做了一些相当复杂的事情。

于 2008-10-03T19:43:25.257 回答
0

您不必放弃类似 Java 的接口并同时使用模板。 Josh对通用基础模板 Container 的建议当然允许您以多态方式传递 Container 及其子项,但另外您当然可以将接口实现为抽象类作为包含的项目。您没有理由不能按照您的建议创建一个抽象的 IComparable 类,这样您就可以拥有如下的多态函数:

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

此方法现在可以采用包含任何实现 IComparable 的类的 Container 的任何子级,因此它非常灵活。

于 2008-10-03T19:47:14.233 回答