C++ 为什么我们不能声明 std::vector<AbstractClass>?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2160920/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why can't we declare a std::vector<AbstractClass>?
提问by BlueTrin
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 ?
这是 C++ 明智的可接受的解决方法吗?如果没有,是否有像 boost 这样的第三方库可以帮助我解决这个问题?
Thank you for reading this !
谢谢您阅读此篇 !
Anthony
安东尼
回答by Georg Fritzsche
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.
这也允许您实际使用多态行为——即使类不是抽象的,按值存储也会导致对象切片的问题。
回答by BlueTrin
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.
您不能创建抽象类类型的向量,因为您不能创建抽象类的实例,并且像 std::vector 这样的 C++ 标准库容器存储值(即实例)。如果你想这样做,你必须创建一个指向抽象类类型的指针向量。
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 的Accelerated C++。
回答by Sergey Teplyakov
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>";
}
};
回答by Matthieu M.
The traditional alternative is to use a vector
of pointers, like already noted.
传统的替代方法是使用 a vector
of 指针,就像已经提到的那样。
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
在性能和接口方面都明显优于智能指针的a 。
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.
当然,它仅适用于您不希望直接操作“真实”对象并且涉及深复制的情况。所以它可能不是你想要的。
回答by kennytm
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.
您可以按照其他建议使用指针。
回答by Eric
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.
std::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
我认为通过您的解决方法,您将能够编译一个,vector<IFunnyInterface>
但您将无法在其中操作 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”在堆上分配内存并将指针存储在 vector<IFunnyInterface*>
回答by David Gruzman
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.
我认为这个真正可悲的限制的根本原因是构造函数不能虚拟。因此,编译器无法在不知道对象在编译时的时间的情况下生成复制对象的代码。