简体   繁体   English

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

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

It looks like I had a fundamental misunderstanding about C++ :< 看来我对C ++有基本的误解:

I like the polymorphic container solution. 我喜欢多态容器解决方案。 Thank you SO, for bringing that to my attention :) 谢谢你,让我引起注意:)


So, we have a need to create a relatively generic container type object. 因此,我们需要创建一个相对通用的容器类型对象。 It also happens to encapsulate some business related logic. 它还碰巧封装了一些与业务相关的逻辑。 However, we need to store essentially arbitrary data in this container - everything from primitive data types to complex classes. 但是,我们需要在该容器中存储基本上任意的数据-从原始数据类型到复杂类的所有内容。

Thus, one would immediately jump to the idea of a template class and be done with it. 因此,将立即跳到模板类的想法并完成它。 However, I have noticed C++ polymorphism and templates do not play well together. 但是,我注意到C ++多态性和模板不能很好地配合使用。 Being that there is some complex logic that we are going to have to work, I would rather just stick with either templates OR polymorphism, and not try to fight C++ by making it do both. 由于存在一些复杂的逻辑我们需要工作,因此我宁愿坚持使用模板或多态性,而不是尝试通过同时使用两者来对抗C ++。

Finally, given that I want to do one or the other, I would prefer polymorphism. 最后,鉴于我想做一个或另一个,我更喜欢多态。 I find it much easier to represent constraints like "this container contains Comparable types" - a la java. 我发现代表“此容器包含可比较类型”之类的约束要容易得多-一个la java。

Bringing me to the topic of question: At the most abstract, I imagine that I could have a "Container" pure virtual interface that has something akin to "push(void* data) and pop(void* data)" (for the record, I am not actually trying to implement a stack). 带给我一个问题:最抽象的说,我想我可以拥有一个“容器”纯虚拟接口,该接口类似于“ push(void * data)和pop(void * data)”(用于记录) ,我实际上并不是在尝试实现堆栈)。

However, I don't really like void* at the top level, not to mention the signature is going to change every time I want to add a constraint to the type of data a concrete container can work with. 但是,我真的不喜欢顶层的void *,更不用说每次我想对具体容器可以使用的数据类型添加约束时,签名都会改变的情况。

Summarizing: We have relatively complex containers that have various ways to retrieve elements. 总结:我们有相对复杂的容器,可以通过多种方式检索元素。 We want to be able to vary the constraints on the elements that can go into the containers. 我们希望能够改变对可以放入容器的元素的约束。 Elements should work with multiple kinds of containers (so long as they meet the constraints of that particular container). 元素应与多种容器一起工作(只要它们满足该特定容器的约束)。

Edit: I should also mention that the containers themselves need to be polymorphic. 编辑:我还应该提到容器本身需要是多态的。 That is my primary reason for not wanting to use templated C++. 这是我不想使用模板化C ++的主要原因。

So - should I drop my love for Java type interfaces and go with templates? 所以-我应该放弃对Java类型接口的热爱并选择模板吗? Should I use void* and statically cast everything? 我应该使用void *并静态转换所有内容吗? Or should I go with an empty class definition "Element" that declares nothing and use that as my top level class in the "Element" hierarchy? 还是应该使用不声明任何内容的空类定义“元素”,并将其用作“元素”层次结构中的顶级类?

One of the reasons why I love stack overflow is that many of the responses provide some interesting insight on other approaches that I hadn't not have even considered. 我之所以喜欢堆栈溢出,原因之一是许多响应对我什至没有考虑过的其他方法提供了一些有趣的见解。 So thank you in advance for your insights and comments. 因此,在此先感谢您的见解和评论。

You can look at using a standard container of boost::any if you are storing truly arbitrary data into the container. 如果要将真正任意的数据存储到容器中,可以使用标准的boost :: any容器。

It sounds more like you would rather have something like a boost::ptr_container where anything that can be stored in the container has to derive from some base type, and the container itself can only give you reference's to the base type. 听起来更像是您希望拥有一个boost :: ptr_container之类的东西,其中可以存储在容器中的任何东西必须源自某个基本类型,并且容器本身只能为您提供对基本类型的引用。

Polymorphism and templates do play very well together, if you use them correctly. 如果正确使用多态和模板,它们可以很好地协同工作。

Anyway, I understand that you want to store only one type of objects in each container instance. 无论如何,我知道您只想在每个容器实例中存储一种类型的对象。 If so, use templates. 如果是这样,请使用模板。 This will prevent you from storing the wrong object type by mistake. 这将防止您错误地存储错误的对象类型。

As for container interfaces: Depending on your design, maybe you'll be able to make them templated, too, and then they'll have methods like void push(T* new_element) . 至于容器接口:根据您的设计,也许您也可以使它们模板化,然后它们将具有诸如void push(T* new_element) Think of what you'll know about the object when you want to add it to a container (of an unknown type). 想想将对象添加到(未知类型)容器中时对对象的了解。 Where will the object come from in the first place? 该对象首先来自何处? A function that returns void* ? 返回void*函数 Do you know that it'll be Comparable? 您知道它可比吗? At least, if all stored object classes are defined in your code, you can make them all inherit from a common ancestor, say, Storable , and use Storable* instead of void* . 至少,如果在代码中定义了所有存储的对象类,则可以使它们全部继承自一个共同的祖先,例如Storable ,并使用Storable*代替void*

Now if you see that objects will always be added to a container by a method like void push(Storable* new_element) , then really there will be no added value in making the container a template. 现在,如果您看到将始终通过void push(Storable* new_element)类的方法将对象添加到容器中,那么实际上,将容器作为模板不会有任何附加值。 But then you'll know it should store Storables. 但是,您将知道它应该存储可存储对象。

The simple thing is to define an abstract base class called Container , and subclass it for each kind of item you may wish to store. 简单的事情是定义一个名为Container的抽象基类,并为您可能希望存储的每种商品子类化。 Then you can use any standard collection class ( std::vector , std::list , etc.) to store pointers to Container . 然后,您可以使用任何标准集合类( std::vectorstd::list等)来存储指向Container指针。 Keep in mind, that since you would be storing pointers, you would have to handle their allocation/deallocation. 请记住,由于将要存储指针,因此必须处理它们的分配/取消分配。

However, the fact that you need a single collection to store objects of such wildly different types is an indication that something may be wrong with the design of your application. 但是,您只需要一个集合来存储这种类型迥异的对象,这一事实表明,您的应用程序设计可能有问题。 It may be better to revisit the business logic before you implement this super-generic container. 在实现此超通用容器之前,最好重新访问业务逻辑。

First, of all, templates and polymorphism are orthogonal concepts and they do play well together. 首先,模板和多态是正交的概念,它们可以很好地协同工作。 Next, why do you want a specific data structure? 接下来,为什么要特定的数据结构? What about the STL or boost data structures (specifically pointer containter ) doesn't work for you. STL或boost数据结构(特别是指针容器 )怎么办对您不起作用。

Given your question, it sounds like you would be misusing inheritance in your situation. 给定您的问题,听起来您会在情况中滥用继承。 It's possible to create " constraints " on what goes in your containers, especially if you are using templates. 可以对容器中的内容创建“ 约束 ”,尤其是在使用模板的情况下。 Those constraints can go beyond what your compiler and linker will give you. 这些限制可能超出了编译器和链接器所能提供的范围。 It's actually more awkward to that sort of thing with inheritance and errors are more likely left for run time. 那种带有继承的东西实际上更尴尬,并且错误更可能留给运行时。

Can you not have a root Container class that contains elements: 您是否没有包含以下元素的根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();
       }
    }
};

These containers can then be passed around 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();

This gives you the Container interface in a type-safe generic way. 这以类型安全的通用方式为您提供了Container接口。

If you want to add constraints to the type of elements that can be contained, you can do something like this: 如果要对可包含的元素类型添加约束,则可以执行以下操作:

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

Using polymorphism, you are basically left with a base class for the container, and derived classes for the data types. 使用多态性,基本上只剩下容器的基类和数据类型的派生类。 The base class/derived classes can have as many virtual functions as you need, in both directions. 基类/派生类可以在两个方向上具有任意数量的虚函数。

Of course, this would mean that you would need to wrap the primitive data types in derived classes as well. 当然,这意味着您还需要将原始数据类型包装在派生类中。 If you would reconsider the use of templates overall, this is where I would use the templates. 如果您要重新考虑整体模板的使用,这就是我要使用模板的地方。 Make one derived class from the base which is a template, and use that for the primitive data types (and others where you don't need any more functionality than is provided by the template). 从作为模板的基础创建一个派生类,并将其用于原始数据类型(以及其他一些不需要模板提供的功能的类)。

Don't forget that you might make your life easier by typedefs for each of the templated types -- especially if you later need to turn one of them into a class. 不要忘记,通过每种模板化类型的typedef可以使您的生活更轻松-特别是如果您以后需要将其中一种转换为类时,尤其如此。

You might also want to check out The Boost Concept Check Library (BCCL) which is designed to provide constraints on the template parameters of templated classes, your containers in this case. 您可能还需要检出Boost Concept检查库(BCCL) ,该旨在对模板化类的模板参数(在这种情况下为您的容器提供约束。

And just to reiterate what others have said, I've never had a problem mixing polymorphism and templates, and I've done some fairly complex stuff with them. 只是重申一下其他人所说的话,我从来没有遇到过将多态和模板混合在一起的问题,并且我对它们做了一些相当复杂的工作。

You could not have to give up Java-like interfaces and use templates as well. 您不必放弃类似Java的界面,也不必使用模板。 Josh's suggestion of a generic base template Container would certainly allow you do polymorphically pass Containers and their children around, but additionally you could certainly implement interfaces as abstract classes to be the contained items. Josh关于通用基本模板Container 的建议当然可以使您多态地传递Container及其子对象,但是除此之外,您当然可以将接口实现为抽象类,以将其包含在内。 There's no reason you couldn't create an abstract IComparable class as you suggested, such that you could have a polymorphic function as follows: 没有理由不能像您建议的那样创建抽象的IComparable类,这样就可以具有如下所示的多态函数:

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

This method can now take any child of Container that contains any class implementing IComparable, so it would be extremely flexible. 现在,此方法可以接受包含实现IComparable的任何类的Container的任何子级,因此它将非常灵活。

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

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