繁体   English   中英

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

[英]C++ alternatives to void* pointers (that isn't templates)

看来我对C ++有基本的误解:

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


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

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

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

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

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

总结:我们有相对复杂的容器,可以通过多种方式检索元素。 我们希望能够改变对可以放入容器的元素的约束。 元素应与多种容器一起工作(只要它们满足该特定容器的约束)。

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

所以-我应该放弃对Java类型接口的热爱并选择模板吗? 我应该使用void *并静态转换所有内容吗? 还是应该使用不声明任何内容的空类定义“元素”,并将其用作“元素”层次结构中的顶级类?

我之所以喜欢堆栈溢出,原因之一是许多响应对我什至没有考虑过的其他方法提供了一些有趣的见解。 因此,在此先感谢您的见解和评论。

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

听起来更像是您希望拥有一个boost :: ptr_container之类的东西,其中可以存储在容器中的任何东西必须源自某个基本类型,并且容器本身只能为您提供对基本类型的引用。

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

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

至于容器接口:根据您的设计,也许您也可以使它们模板化,然后它们将具有诸如void push(T* new_element) 想想将对象添加到(未知类型)容器中时对对象的了解。 该对象首先来自何处? 返回void*函数 您知道它可比吗? 至少,如果在代码中定义了所有存储的对象类,则可以使它们全部继承自一个共同的祖先,例如Storable ,并使用Storable*代替void*

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

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

但是,您只需要一个集合来存储这种类型迥异的对象,这一事实表明,您的应用程序设计可能有问题。 在实现此超通用容器之前,最好重新访问业务逻辑。

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

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

您是否没有包含以下元素的根Container类:

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

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

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

不要忘记,通过每种模板化类型的typedef可以使您的生活更轻松-特别是如果您以后需要将其中一种转换为类时,尤其如此。

您可能还需要检出Boost Concept检查库(BCCL) ,该旨在对模板化类的模板参数(在这种情况下为您的容器提供约束。

只是重申一下其他人所说的话,我从来没有遇到过将多态和模板混合在一起的问题,并且我对它们做了一些相当复杂的工作。

您不必放弃类似Java的界面,也不必使用模板。 Josh关于通用基本模板Container 的建议当然可以使您多态地传递Container及其子对象,但是除此之外,您当然可以将接口实现为抽象类,以将其包含在内。 没有理由不能像您建议的那样创建抽象的IComparable类,这样就可以具有如下所示的多态函数:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM