为什么我们不能声明std::vector?

时间:2021-08-16 17:29:42

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#中花费了相当长的时间,我注意到如果您声明一个抽象类,目的是使用它作为接口,那么您就不能实例化这个抽象类的向量来存储子类的实例。

#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:

在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 ? If not, is there any third party library like boost which could help me to get around this ?

这是一个可以接受的解决方案吗?如果没有的话,有没有第三方图书馆可以帮助我解决这个问题?

Thank you for reading this !

感谢您阅读本文!

Anthony

安东尼

7 个解决方案

#1


93  

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.

这也允许您实际使用多态行为——即使类不是抽象的,按值存储也会导致对象切片的问题。

#2


18  

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 (i.e. instances). If you want to do this, you will have to create a vector of pointers to the abstract class type.

不能创建抽象类类型的向量,因为不能创建抽象类的实例,也不能创建c++标准库容器,如std::vector存储值(即实例)。如果您想这样做,您必须创建一个指向抽象类类型的指针向量。

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. 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#几乎没有共同之处。如果您打算学习c++,您应该把它看作是从零开始,并阅读一本优秀的c++教程,比如Koenig和Moo的《加速c++》。

#3


6  

The traditional alternative is to use a vector of pointers, like already noted.

传统的替代方法是使用指针向量,如前所述。

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带来了一个非常有趣的库:指针容器,它非常适合于任务,并使您从指针所暗示的各种问题中解脱出来:

  • 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.

注意,无论在性能还是接口方面,这都比智能指针的矢量要好得多。

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习语的变体。

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.

当然,它只在您不希望直接操作“true”对象并涉及深度拷贝的情况下才有效。所以这可能不是你想要的。

#4


5  

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之间没有关系,并且由于私有继承,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>";
    }
};

#5


2  

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.

您可以像其他建议那样使用指针。

#6


1  

std::vector will try to allocate memory to contain your type. If your class is purely virtual, the vector cannot know the size of the class it will have to allocate.

vector将尝试分配内存来包含您的类型。如果你的类是纯虚拟的,那么向量就不能知道它要分配的类的大小。

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. 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

我认为通过您的解决方案,您将能够编译一个向量 ,但是您将无法在其中操作FunnyImpl。例如,如果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”在堆上分配内存,并在向量 中存储指针 *>

#7


-2  

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.

我认为造成这种可悲限制的根本原因是构造函数不能虚拟。编译器在编译时不知道对象的时间就不能生成复制对象的代码。

#1


93  

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.

这也允许您实际使用多态行为——即使类不是抽象的,按值存储也会导致对象切片的问题。

#2


18  

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 (i.e. instances). If you want to do this, you will have to create a vector of pointers to the abstract class type.

不能创建抽象类类型的向量,因为不能创建抽象类的实例,也不能创建c++标准库容器,如std::vector存储值(即实例)。如果您想这样做,您必须创建一个指向抽象类类型的指针向量。

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. 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#几乎没有共同之处。如果您打算学习c++,您应该把它看作是从零开始,并阅读一本优秀的c++教程,比如Koenig和Moo的《加速c++》。

#3


6  

The traditional alternative is to use a vector of pointers, like already noted.

传统的替代方法是使用指针向量,如前所述。

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带来了一个非常有趣的库:指针容器,它非常适合于任务,并使您从指针所暗示的各种问题中解脱出来:

  • 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.

注意,无论在性能还是接口方面,这都比智能指针的矢量要好得多。

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习语的变体。

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.

当然,它只在您不希望直接操作“true”对象并涉及深度拷贝的情况下才有效。所以这可能不是你想要的。

#4


5  

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之间没有关系,并且由于私有继承,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>";
    }
};

#5


2  

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.

您可以像其他建议那样使用指针。

#6


1  

std::vector will try to allocate memory to contain your type. If your class is purely virtual, the vector cannot know the size of the class it will have to allocate.

vector将尝试分配内存来包含您的类型。如果你的类是纯虚拟的,那么向量就不能知道它要分配的类的大小。

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. 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

我认为通过您的解决方案,您将能够编译一个向量 ,但是您将无法在其中操作FunnyImpl。例如,如果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”在堆上分配内存,并在向量 中存储指针 *>

#7


-2  

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.

我认为造成这种可悲限制的根本原因是构造函数不能虚拟。编译器在编译时不知道对象的时间就不能生成复制对象的代码。