怎么你宣布一个接口在C++?
-
11-07-2019 - |
题
我怎么设置的一类,表示接口?这只是一个抽象的基类?
解决方案
要由 bradtgmurray 本答案扩大,你可能希望通过增加一个虚析构函数,使一个例外,你的接口的纯虚方法列表。这使您可以通过指针所有权转让给另一方不暴露具体的派生类。析构函数不必做任何事情,因为该接口没有任何具体的成员。这似乎矛盾的定义函数为虚拟和内联,但请相信我 - 它不是
class IDemo
{
public:
virtual ~IDemo() {}
virtual void OverrideMe() = 0;
};
class Parent
{
public:
virtual ~Parent();
};
class Child : public Parent, public IDemo
{
public:
virtual void OverrideMe()
{
//do stuff
}
};
您不必包括身体为虚析构函数 - 事实证明,一些编译器有麻烦优化空的析构函数和你最好不要使用默认的。
其他提示
请用纯虚拟方法的类。通过创建另一个类中的覆盖那些虚拟方法使用的接口。
一个纯虚方法是被定义为虚拟并且分配给0类方法。
class IDemo
{
public:
virtual ~IDemo() {}
virtual void OverrideMe() = 0;
};
class Child : public IDemo
{
public:
virtual void OverrideMe()
{
//do stuff
}
};
整个原因,你有一个特殊的口型类别中除了抽象的基类在C#/Java 是因为C#/Java不支持多个继承。
C++支持多个继承等特殊类型是不是需要的。一个抽象的基类没有非抽象(纯粹的虚拟的)方法在功能上等同于一个C#/Java接口。
没有“接口”的概念本身在C ++中。据我所知,接口在Java中被首次引入到解决缺乏多重继承的。这一概念通过使用一个抽象基类被证明是非常有用的,并且相同的效果可以在C ++实现。
一个抽象基类是一类,其中至少一个成员函数(在Java中行话方法)是使用以下语法纯虚函数声明:
class A
{
virtual void foo() = 0;
};
一个抽象基类不能被实例化,也就是即你不能声明A类的一个对象只能从一个派生的类,但不提供foo()
的实现任何派生类也将是抽象的。为了停止正在抽象,派生类必须为它继承所有纯虚函数提供实现。
请注意,一个抽象基类可以比接口更多,因为它可以包含数据成员和不是纯虚成员函数。的界面的当量将是一个抽象基类,而不仅用纯虚函数的任何数据。
和,马克兰塞姆指出的那样,一个抽象基类应该提供一个虚析构函数,就像任何基类,对于这个问题。
据我可以测试,这是非常重要的添加虚拟析构函数。我使用与new
创建并与delete
损坏的对象。
如果您没有在界面中添加虚拟析构函数,那么继承类的析构函数没有被调用。
class IBase {
public:
virtual ~IBase() {}; // destructor, use it to call destructor of the inherit classes
virtual void Describe() = 0; // pure virtual method
};
class Tester : public IBase {
public:
Tester(std::string name);
virtual ~Tester();
virtual void Describe();
private:
std::string privatename;
};
Tester::Tester(std::string name) {
std::cout << "Tester constructor" << std::endl;
this->privatename = name;
}
Tester::~Tester() {
std::cout << "Tester destructor" << std::endl;
}
void Tester::Describe() {
std::cout << "I'm Tester [" << this->privatename << "]" << std::endl;
}
void descriptor(IBase * obj) {
obj->Describe();
}
int main(int argc, char** argv) {
std::cout << std::endl << "Tester Testing..." << std::endl;
Tester * obj1 = new Tester("Declared with Tester");
descriptor(obj1);
delete obj1;
std::cout << std::endl << "IBase Testing..." << std::endl;
IBase * obj2 = new Tester("Declared with IBase");
descriptor(obj2);
delete obj2;
// this is a bad usage of the object since it is created with "new" but there are no "delete"
std::cout << std::endl << "Tester not defined..." << std::endl;
descriptor(new Tester("Not defined"));
return 0;
}
如果您运行不virtual ~IBase() {};
前面的代码,你会看到,析构函数Tester::~Tester()
永远不会被调用。
我的回答基本上是相同的其他人,但我认为有两个其他重要的事情要做到:
声明一个虚拟析构在您的接口,或者使受保护的非虚拟的,以避免不确定的行为,如果有人试图删除的对象的类型
IDemo
.使用虚拟继承,以避免问题的蒙山的多个继承。(有更多的往往多个继承,当我们使用的接口。)
像其他回答说:
- 使一级的与纯粹的虚拟方法。
使用的界面,创造另一类别复盖的那些虚拟方法。
class IDemo { public: virtual void OverrideMe() = 0; virtual ~IDemo() {} }
或
class IDemo { public: virtual void OverrideMe() = 0; protected: ~IDemo() {} }
和
class Child : virtual public IDemo { public: virtual void OverrideMe() { //do stuff } }
C++11你可以很容易地避免的继承:
struct Interface {
explicit Interface(SomeType& other)
: foo([=](){ return other.my_foo(); }),
bar([=](){ return other.my_bar(); }), /*...*/ {}
explicit Interface(SomeOtherType& other)
: foo([=](){ return other.some_foo(); }),
bar([=](){ return other.some_bar(); }), /*...*/ {}
// you can add more types here...
// or use a generic constructor:
template<class T>
explicit Interface(T& other)
: foo([=](){ return other.foo(); }),
bar([=](){ return other.bar(); }), /*...*/ {}
const std::function<void(std::string)> foo;
const std::function<void(std::string)> bar;
// ...
};
在这种情况下,一个接口已经参考义,即你必须确保对象的虚荣心的接口(它还可能使接口与价值语).
这些类型的接口有自己的优点和缺点:
- 他们 需要更多的存储器 于继承权基于多态性。
- 他们 是在一般的速度更快 于继承权基于多态性。
- 在这些案件中,你知道最后的类型 他们更快! (一些编译器喜欢的海湾合作委员会和铛执行更为优化的类型,不必/继承类型的虚拟功能)。
最后,遗产是所有罪恶的根源在复杂的软件设计。在 肖恩的父母的价值语和概念基于多 (强烈建议,更好的版本的这种技术是解释有)以下情况下是行了研究:
说我有一个应用程序中,我处理我的形状多态方式使用 MyShape
接口:
struct MyShape { virtual void my_draw() = 0; };
struct Circle : MyShape { void my_draw() { /* ... */ } };
// more shapes: e.g. triangle
在你的申请,您这样做与不同的形状,使用 YourShape
接口:
struct YourShape { virtual void your_draw() = 0; };
struct Square : YourShape { void your_draw() { /* ... */ } };
/// some more shapes here...
现在说你想用的某些形状,我已经开发应用程序。从概念上讲,我们的形状都相同的接口,但是要让我的形状的工作在应用程序将需要延长我的形状如下:
struct Circle : MyShape, YourShape {
void my_draw() { /*stays the same*/ };
void your_draw() { my_draw(); }
};
第一,修改我的形状可能是不可能的。此外,多个继承导致的道路的意大利面条码(设想的第三个项目都在使用的 TheirShape
接口...会发生什么,如果他们还呼吁他们的绘画功能 my_draw
?).
更新:有几个新的参考关于非继承权基于多态性:
- 肖恩的父母的 继承权是基类的邪恶的 谈。
- 肖恩的父母的 值语和概念基于多 谈。
- Pyry Jahkola的 继承自由多 谈及 聚库的文档.
- 扎克*莱恩的 实际类型删除:解决面向对象的问题有一个优雅的设计图案 谈。
- 安杰伊*C++的博客型删除的部分 我, ii, iii, , iv.
- 运行多晶型通用程序混对象和概念在ConceptC++
- 提升。TypeErasure文档
- Adobe聚文档
- 提升。任何, std::任何提案(修订版3), 提升。精神::hold_any.
以上所有好的答案。 你应该记住的一个额外的东西 - 你也可以有一个纯虚析构函数。唯一的区别是,你仍然需要实现它。
困惑?
--- header file ----
class foo {
public:
foo() {;}
virtual ~foo() = 0;
virtual bool overrideMe() {return false;}
};
---- source ----
foo::~foo()
{
}
你会想这样做的主要原因是,如果你想要提供接口的方法,因为我有,但要覆盖这些可选。
要作出一个接口类需要一个纯虚方法的类,但是所有的虚拟方法具有默认实现,因此唯一的方法左使纯虚是析构函数。
重新实现在派生类中的析构函数没有什么大不了, - 我总是重新实现析构函数,虚拟或不是,在我的派生类
。如果您正在使用微软的C ++编译器,那么你可以做到以下几点:
struct __declspec(novtable) IFoo
{
virtual void Bar() = 0;
};
class Child : public IFoo
{
public:
virtual void Bar() override { /* Do Something */ }
}
我喜欢这种方法,因为它导致大量的更小的接口代码和所生成的代码的大小可以是显著小。使用novtable的删除所有参考这个类的虚函数表的指针,这样你就可以永远无法直接实例它。在这里看到的文档 - novtable
一个小除了什么写在那里:
首先,确保你的析构函数也是纯虚
其次,你可能要继承几乎(而不是通常)当你实现,只是为了好措施。
您也可以考虑与NVI(非虚拟接口模式)实现的合约类。例如:
struct Contract1 : boost::noncopyable
{
virtual ~Contract1();
void f(Parameters p) {
assert(checkFPreconditions(p)&&"Contract1::f, pre-condition failure");
// + class invariants.
do_f(p);
// Check post-conditions + class invariants.
}
private:
virtual void do_f(Parameters p) = 0;
};
...
class Concrete : public Contract1, public Contract2
{
private:
virtual void do_f(Parameters p); // From contract 1.
virtual void do_g(Parameters p); // From contract 2.
};
我仍然在C ++开发新的。我开始与Visual Studio(VS)。
然而,似乎没有人在VS的(NET)以提到的__interface
。我的不可以非常肯定,如果这是声明的接口的好方法。但它似乎提供的附加执行的(在提到的文件的)。这样你就不必明确指定virtual TYPE Method() = 0;
,因为它会自动转换。
__interface IMyInterface {
HRESULT CommitX();
HRESULT get_X(BSTR* pbstrName);
};
不过,我不使用它,因为我对跨平台编译兼容性问题,因为它只提供.NET下。
如果有人做有什么有趣的话,请分享。 : - )
感谢。
虽然这是真的, virtual
是的事实上的标准来定义的一个接口,让我们不要忘记经典的C类似的模式,其中设有一个构造C++:
struct IButton
{
void (*click)(); // might be std::function(void()) if you prefer
IButton( void (*click_)() )
: click(click_)
{
}
};
// call as:
// (button.*click)();
这具有的优点在于你可以重新结合事件的运行时没有必要建立类再次(C++没有语法更改多态的类型,这是一个解决方法的变色龙类)。
提示:
- 你可能会继承这样一个基类(虚拟和非虚拟的都是允许的),并填写
click
在你后裔的构造。 - 你可能会有功能的指针作为一个
protected
成员和有public
参考和/或吸气。 - 如上所述,这可以让你关执行在运行时间。因此,它是一种方法来管理国家。根据数
if
s与国家改变你的代码,这个 可能会 以更快的速度比switch()
es或if
s(周转,预计大约3-4if
s,但总是衡量第一次。 - 如果你选择
std::function<>
在功能的指针,你 可能会 能够管理所有对象内的数据IBase
.从这一点上,你可以有价值图为IBase
(例如,std::vector<IBase>
将工作)。注意这个 可能会 慢取决于你的编译器和STL码;也是当前实现的std::function<>
往往有的开销比较时,以功能的指针,或甚至是虚拟的功能(这可能会改变未来).
下面是在c abstract class
的定义++标准
n4687
<强> 13.4.2 强>
一个抽象类是可以仅使用作为基类一些其它类中的一个;没有一个抽象的对象 可以除非从它派生的类的子对象来创建的类。一类是抽象的,如果它有至少 一个纯虚函数。
class Shape
{
public:
// pure virtual function providing interface framework.
virtual int getArea() = 0;
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};
class Triangle: public Shape
{
public:
int getArea()
{
return (width * height)/2;
}
};
int main(void)
{
Rectangle Rect;
Triangle Tri;
Rect.setWidth(5);
Rect.setHeight(7);
cout << "Rectangle area: " << Rect.getArea() << endl;
Tri.setWidth(5);
Tri.setHeight(7);
cout << "Triangle area: " << Tri.getArea() << endl;
return 0;
}
结果: 矩形区域:35 三角形的面积:17
我们已经看到了如何一个抽象类中的getArea()和其它两个类中的术语实现的相同功能,但具有不同算法定义的接口来计算特定的形状的区域。