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

Blu*_*rin 78 c++ abstract-class stl

花了很长时间在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;
};
Run Code Online (Sandbox Code Playgroud)

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

error C2259: 'IFunnyInterface' : cannot instantiate abstract class
Run Code Online (Sandbox Code Playgroud)

我看到一个明显的解决方法,即用以下内容替换IFunnyInterface:

class IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        throw new std::exception("not implemented");
    }
};
Run Code Online (Sandbox Code Playgroud)

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

谢谢您阅读此篇 !

安东尼

Geo*_*che 116

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

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

std::vector<IFunnyInterface*> ifVec;
Run Code Online (Sandbox Code Playgroud)

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

  • 或者现在,std :: vector <std :: unique_ptr <IFunnyInterface >>. (7认同)
  • 或者您可以使用std :: vector <std :: tr1 :: shared_ptr <IFunnyInterface >>,如果您不想手动处理对象生存期. (5认同)
  • 甚至更好,boost :: ptr_vector <>. (4认同)

小智 21

您无法创建抽象类类型的向量,因为您无法创建抽象类的实例,以及像std :: vector存储值(即实例)的C++标准库容器.如果要执行此操作,则必须创建指向抽象类类型的指针向量.

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

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


Ser*_*kov 6

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

std::vector <IFunnyInterface*> funnyItems;
Run Code Online (Sandbox Code Playgroud)

要么

std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;
Run Code Online (Sandbox Code Playgroud)

因为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>";
    }
};
Run Code Online (Sandbox Code Playgroud)


Mat*_* M. 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 { .. };
Run Code Online (Sandbox Code Playgroud)

这非常简单,并且Pimpl通过Strategy模式丰富了成语的变体.

当然,它仅适用于您不希望直接操作"真实"对象并涉及深层复制的情况.所以它可能不是你想要的.