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

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

在MS VS2005中声明抽象类向量的行会导致这个错误:

 error C2259: 'IFunnyInterface' : cannot instantiate abstract class 

我看到一个明显的解决方法,即用下面的代替IFunnyInterface:

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

这是一个可接受的解决方法C ++明智吗? 如果没有,是否有任何第三方库像boost可以帮助我解决这个问题?

感谢您阅读本文!

安东尼

你不能实例化抽象类,因此抽象类的向量不能工作。

但是,您可以使用指向抽象类的向量:

 std::vector<IFunnyInterface*> ifVec; 

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

你不能创build一个抽象类types的向量,因为你不能创build抽象类的实例,而C ++标准库容器就像std :: vector存储值(即实例)。 如果你想这样做,你将不得不创build一个指向抽象类types的指针向量。

你的workround将无法工作,因为虚拟函数(这就是为什么你想抽象类首先)只有通过指针或引用调用时才工作。 你也不能创build引用的向量,所以这是你必须使用一个指针向量的第二个原因。

你应该认识到C ++和C#有很less的共同点。 如果你打算学习C ++,你应该把它看作是从头开始,阅读一个好的专门的C ++教程,例如Koenig和Moo的Accelerated C ++ 。

传统的select是使用指针vector ,就像已经提到的那样。

对于那些赞赏的人来说, Boost带来了一个非常有趣的库: Pointer Containers是非常适合这项任务,并将您从指针所隐含的各种问题中解放出来:

  • 终生pipe理
  • 迭代器的双重解引用

请注意,这在性能和接口方面都明显优于智能指针vector

现在,有第三个select,这是改变你的层次结构。 为了更好地保护用户,我已经多次看到以下模式:

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

这是相当简单的,是一种Strategy模式丰富的PimplPimpl的变化。

当然,只有当你不希望直接操纵“真实”的对象,并涉及深层复制时,它才起作用。 所以这可能不是你想要的。

在这种情况下,我们甚至不能使用这个代码:

 std::vector <IFunnyInterface*> funnyItems; 

要么

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

因为在FunnyImpl和IFunnyInterface之间没有IS关系,并且由于私有inheritance,在FUnnyImpl和IFunnyInterface之间不存在隐式转换。

你应该更新你的代码如下:

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

因为调整一个vector的大小,你需要使用默认的构造函数和类的大小,这又要求它是具体的。

您可以使用其他build议的指针。

std :: vector将尝试分配内存来包含您的types。 如果你的类是纯粹的虚拟的,vector不能知道它将不得不分配的类的大小。

我认为,用你的解决方法,你将能够编译一个vector<IFunnyInterface>但是你将不能操纵里面的FunnyImpl。 例如,如果IFunnyInterface(抽象类)的大小为20(我真的不知道),并且FunnyImpl的大小为30,因为它具有更多的成员和代码,最终将尝试将30放入20

解决的办法是用“new”在堆上分配内存,并在vector<IFunnyInterface*>存储指针vector<IFunnyInterface*>

我认为这个真正令人伤心的限制的根本原因是构造函数不能虚拟的事实。 因此编译器不能在编译时间内不知道它的时间而生成复制对象的代码。