为什么我们不能声明std :: vector <abstract class>?</abstract class>

时间:2010-01-29 09:17:03

标签: c++ stl abstract-class

花了很长时间在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这样的第三方库可以帮助我解决这个问题?

感谢您阅读本文!

安东尼

7 个答案:

答案 0 :(得分:110)

您无法实例化抽象类,因此抽象类的向量无法工作。

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

std::vector<IFunnyInterface*> ifVec;

这也允许您实际使用多态行为 - 即使该类不是抽象的,按值存储也会导致object slicing的问题。

答案 1 :(得分:19)

您无法创建抽象类类型的向量,因为您无法创建抽象类的实例,以及像std :: vector存储值(即实例)的C ++标准库容器。如果要这样做,则必须创建一个指向抽象类类型的指针向量。

你的workround不起作用,因为虚函数(这就是你想要抽象类的原因)只有在通过指针或引用调用时才有效。您也无法创建引用向量,因此这是您必须使用指针向量的第二个原因。

你应该意识到C ++和C#几乎没有共同之处。如果您打算学习C ++,您应该将其视为从头开始,并阅读Koenig和Moo撰写的一篇专业的C ++教程,如Accelerated C++

答案 2 :(得分:6)

在这种情况下,我们甚至无法使用此代码:

std::vector <IFunnyInterface*> funnyItems;

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

因为FunnyImpl和IFunnyInterface之间没有IS A关系,并且由于私有继承而在FUnnyImpl和IFunnyInterface之间没有隐式转换。

您应该按如下方式更新您的代码:

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

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

答案 3 :(得分:6)

传统的替代方法是使用vector指针,如上所述。

对于那些欣赏的人,Boost附带了一个非常有趣的库:Pointer Containers,它非常适合于任务,并使您免受指针隐含的各种问题的影响:

  • 终身管理
  • 迭代器的双重解除引用

请注意,这在性能和界面方面明显优于vector智能指针。

现在,还有第三种方法,即更改层次结构。为了更好地保护用户,我已经多次看到使用以下模式:

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

这非常简单,Pimpl成语的变体由Strategy模式丰富。

当然,只有在您不希望直接操作“真实”对象且涉及深层复制的情况下,它才有效。所以它可能不是你想要的。

答案 4 :(得分:2)

因为要调整向量的大小,你需要使用默认的构造函数和类的大小,这反过来要求它是具体的。

您可以将指针用作其他建议。

答案 5 :(得分:1)

std :: vector将尝试分配内存以包含您的类型。如果您的类是纯虚拟的,则向量无法知道它必须分配的类的大小。

我认为通过您的解决方法,您将能够编译vector<IFunnyInterface>,但您将无法在其中操纵FunnyImpl。例如,如果IFunnyInterface(抽象类)的大小为20(我真的不知道)而FunnyImpl的大小为30,因为它有更多的成员和代码,那么你最终会尝试将30个放入20的向量中

解决方案是在堆上使用“new”分配内存并在vector<IFunnyInterface*>中存储指针

答案 6 :(得分:-2)

我认为这个真正令人伤心的限制的根本原因是构造函数不能虚拟。其编译器无法生成在不知道编译时间的情况下复制对象的代码。