花了很长时间在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这样的第三方库可以帮助我解决这个问题?
感谢您阅读本文!
安东尼
答案 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)
我认为这个真正令人伤心的限制的根本原因是构造函数不能虚拟。其编译器无法生成在不知道编译时间的情况下复制对象的代码。