void *指针的C ++替代品(不是模板)

时间:2008-10-03 19:10:08

标签: c++ templates pointers polymorphism

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

我喜欢多态容器解决方案。谢谢你,因为我引起了我的注意:)


因此,我们需要创建一个相对通用的容器类型对象。它也恰好封装了一些与业务相关的逻辑。但是,我们需要在这个容器中存储基本上任意的数据 - 从原始数据类型到复杂类。

因此,人们会立即跳到模板类的想法并完成它。但是,我注意到C ++多态性和模板不能很好地结合在一起。由于存在一些我们将要工作的复杂逻辑,我宁愿坚持使用模板或多态,而不是试图通过同时做两者来对抗C ++。

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

让我谈谈问题:最抽象的是,我想我可以拥有一个“容器”纯虚拟界面,它具有类似于“push(void * data)和pop(void * data)”的东西(为了记录,我实际上并没有尝试实现堆栈。)

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

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

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

那么 - 我应该放弃对Java类型接口的热爱并使用模板吗?我应该使用void *并静态地投射一切吗?或者我应该使用一个空类定义“元素”来声明什么,并将其用作“元素”层次结构中的顶级类?

我喜欢堆栈溢出的原因之一是许多响应提供了一些我甚至没有考虑过的其他方法的有趣见解。所以,请提前感谢您的见解和意见。

8 个答案:

答案 0 :(得分:13)

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

这听起来更像是你希望像boost::ptr_container这样的东西,其中可以存储在容器中的任何内容必须从某种基类型派生出来,而容器本身只能给出你引用了基本类型。

答案 1 :(得分:5)

如果正确使用多态性和模板,它们可以很好地协同工作。

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

至于容器界面:根据你的设计,也许你也可以将它们模板化,然后它们会有像void push(T* new_element)这样的方法。当您想要将对象添加到容器(未知类型)时,请考虑您对该对象的了解。这个对象首先来自哪里?返回void*的函数?你知道它会被比较吗?至少,如果在代码中定义了所有存储的对象类,则可以使它们全部继承自共同的祖先,例如Storable,并使用Storable*而不是void*

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

答案 2 :(得分:5)

简单的事情是定义一个名为Container的抽象基类,并为您希望存储的每种项目创建子类。然后,您可以使用任何标准集合类(std::vectorstd::list等)来存储指向Container的指针。请记住,既然你要存储指针,就必须处理它们的分配/释放。

但是,您需要单个集合来存储这种截然不同的类型的对象这一事实表明应用程序的设计可能存在问题。在实现这个超级通用容器之前,最好重新检查业务逻辑。

答案 3 :(得分:3)

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

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

答案 4 :(得分: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
};

答案 5 :(得分:1)

使用多态性,您基本上可以使用容器的基类,以及数据类型的派生类。基类/派生类可以在两个方向上拥有所需数量的虚函数。

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

不要忘记,你可以通过typedef为每个模板化类型让你的生活更轻松 - 特别是如果你以后需要将其中一个变成一个类。

答案 6 :(得分:1)

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

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

答案 7 :(得分:0)

您不必放弃类似Java的界面并使用模板。通用基本模板容器Josh's suggestion肯定允许您以多态方式传递容器及其子代,但另外您可以将接口实现为抽象类作为包含的项。没有理由你不能像你建议的那样创建一个抽象的IComparable类,这样你就可以拥有如下的多态函数:

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

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