简体   繁体   English

为什么我们不能声明一个 std::vector<abstractclass> ?</abstractclass>

[英]Why can't we declare a std::vector<AbstractClass>?

Having spent quite some time developping in C#, I noticed that if you declare an abstract class for the purpose of using it as an interface you cannot instantiate a vector of this abstract class to store instances of the children classes.在 C# 中花费了相当长的时间进行开发后,我注意到如果您声明一个抽象 class 以将其用作接口,则您无法实例化此抽象 class 的向量来存储子类的实例。

#pragma once
#include <iostream>
#include <vector>

using namespace std;

class IFunnyInterface
{
public:
    virtual void IamFunny()  = 0;
};

class FunnyImpl: IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        cout << "<INSERT JOKE HERE>";
    }
};

class FunnyContainer
{
private:
    std::vector <IFunnyInterface> funnyItems;
};

The line declaring the vector of abstract class causes this error in MS VS2005:声明抽象 class 向量的行在 MS VS2005 中导致此错误:

error C2259: 'IFunnyInterface' : cannot instantiate abstract class

I see an obvious workaround, which is to replace IFunnyInterface with the following:我看到了一个明显的解决方法,即将 IFunnyInterface 替换为以下内容:

class IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        throw new std::exception("not implemented");
    }
};

Is this an acceptable workaround C++ wise?这是一个可接受的解决方法 C++ 明智吗? If not, is there any third party library like boost which could help me to get around this?如果没有,是否有像 boost 这样的第三方库可以帮助我解决这个问题?

Thank you for reading this !谢谢您阅读此篇 !

Anthony安东尼

You can't instantiate abstract classes, thus a vector of abstract classes can't work.你不能实例化抽象类,因此抽象类的向量不能工作。

You can however use a vector of pointers to abstract classes:但是,您可以使用指向抽象类的指针向量:

std::vector<IFunnyInterface*> ifVec;

This also allows you to actually use polymorphic behaviour - even if the class wasn't abstract, storing by value would lead to the problem of object slicing .这也允许您实际使用多态行为——即使类不是抽象的,按值存储也会导致对象切片的问题。

You can't create a vector of an abstract class type because you cannot create instances of an abstract class, and C++ Standard Library containers like std::vector store values (ie instances).您不能创建抽象类类型的向量,因为您不能创建抽象类的实例,并且像 std::vector 这样的 C++ 标准库容器存储值(即实例)。 If you want to do this, you will have to create a vector of pointers to the abstract class type.如果你想这样做,你必须创建一个指向抽象类类型的指针向量。

Your workround would not work because virtual functions (which is why you want the abstract class in the first place) only work when called through pointers or references.您的解决方法将不起作用,因为虚函数(这就是您首先想要抽象类的原因)仅在通过指针或引用调用时才起作用。 You cannot create vectors of references either, so this is a second reason why you must use a vector of pointers.你也不能创建引用向量,所以这是你必须使用指针向量的第二个原因。

You should realise that C++ and C# have very little in common.您应该意识到 C++ 和 C# 几乎没有共同之处。 If you are intending to learn C++, you should think of it as starting from scratch, and read a good dedicated C++ tutorial such as Accelerated C++ by Koenig and Moo.如果您打算学习 C++,您应该将其视为从头开始,并阅读专门的 C++ 教程,例如 Koenig 和 Moo 的Accelerated C++

In this case we can't use even this code:在这种情况下,我们甚至不能使用以下代码:

std::vector <IFunnyInterface*> funnyItems;

or或者

std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;

Because there is no IS A relationship between FunnyImpl and IFunnyInterface and there is no implicit convertion between FUnnyImpl and IFunnyInterface because of private inheritance.因为FunnyImpl 和IFunnyInterface 之间没有IS A 关系,并且由于私有继承,FUnnyImpl 和IFunnyInterface 之间没有隐式转换。

You should update your code as follows:您应该按如下方式更新您的代码:

class IFunnyInterface
{
public:
    virtual void IamFunny()  = 0;
};

class FunnyImpl: public IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        cout << "<INSERT JOKE HERE>";
    }
};

The traditional alternative is to use a vector of pointers, like already noted.传统的替代方法是使用指针vector ,就像已经提到的那样。

For those who appreciate, Boost comes with a very interesting library: Pointer Containers which is perfectly suited for the task and frees you from the various problems implied by pointers:对于那些欣赏的人, Boost附带了一个非常有趣的库: Pointer Containers ,它非常适合该任务,并使您摆脱指针隐含的各种问题:

  • lifetime management终身管理
  • double dereferencing of iterators迭代器的双重解引用

Note that this is significantly better than a vector of smart pointers, both in terms of performance and interface.请注意,这在性能和接口方面都明显优于智能指针vector

Now, there is a 3rd alternative, which is to change your hierarchy.现在,还有第三种选择,即更改您的层次结构。 For better insulation of the user, I have seen a number of times the following pattern used:为了更好地隔离用户,我多次看到使用以下模式:

class IClass;

class MyClass
{
public:
  typedef enum { Var1, Var2 } Type;

  explicit MyClass(Type type);

  int foo();
  int bar();

private:
  IClass* m_impl;
};

struct IClass
{
  virtual ~IClass();

  virtual int foo();
  virtual int bar();
};

class MyClass1: public IClass { .. };
class MyClass2: public IClass { .. };

This is quite straightforward, and a variation of the Pimpl idiom enriched by a Strategy pattern.这非常简单,并且是Pimpl习语的变体,并通过Strategy模式进行了丰富。

It works, of course, only in the case where you do not wish to manipulate the "true" objects directly, and involves deep-copy.当然,它仅适用于您不希望直接操作“真实”对象并且涉及深复制的情况。 So it may not be what you wish.所以它可能不是你想要的。

Because to resize a vector you need to use the default constructor and the size of the class, which in turn requires it to be concrete.因为要调整向量的大小,您需要使用默认构造函数和类的大小,这反过来又要求它是具体的。

You can use a pointer as other suggested.您可以按照其他建议使用指针。

std::vector will try to allocate memory to contain your type. std::vector 将尝试分配内存以包含您的类型。 If your class is purely virtual, the vector cannot know the size of the class it will have to allocate.如果您的类是纯虚拟的,则向量无法知道它必须分配的类的大小。

I think that with your workaround, you will be able to compile a vector<IFunnyInterface> but you won't be able to manipulate FunnyImpl inside of it.我认为通过您的解决方法,您将能够编译vector<IFunnyInterface>但您将无法在其中操作 funnyImpl。 For example if IFunnyInterface (abstract class) is of size 20 (i dont really know) and FunnyImpl is of size 30 because it has more members and code, you will end up trying to fit 30 into your vector of 20例如,如果 IFunnyInterface(抽象类)的大小为 20(我真的不知道),而 funnyImpl 的大小为 30,因为它有更多的成员和代码,您最终将尝试将 30 放入 20 的向量中

The solution would be to allocate memory on the heap with "new" and store pointers in vector<IFunnyInterface*>解决方案是使用“new”在堆上分配内存并将指针存储在vector<IFunnyInterface*>

I think that the root cause of this really sad limitation is the fact that constructors can not virtual.我认为这个真正可悲的限制的根本原因是构造函数不能虚拟。 Thereof compiler can not generate code which copy the object without knowing its time in the compile time.因此,编译器无法在不知道对象在编译时的时间的情况下生成复制对象的代码。

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

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