How do I setup a class that represents an interface? Is this just an abstract base class?
如何设置表示接口的类?这只是一个抽象基类吗?
15 个解决方案
#1
614
To expand on the answer by bradtgmurray, you may want to make one exception to the pure virtual method list of your interface by adding a virtual destructor. This allows you to pass pointer ownership to another party without exposing the concrete derived class. The destructor doesn't have to do anything, because the interface doesn't have any concrete members. It might seem contradictory to define a function as both virtual and inline, but trust me - it isn't.
要扩展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
}
};
You don't have to include a body for the virtual destructor - it turns out some compilers have trouble optimizing an empty destructor and you're better off using the default.
您不需要为虚拟析构函数包含一个主体——它发现一些编译器在优化空析构函数时遇到了麻烦,而您最好使用缺省值。
#2
224
Make a class with pure virtual methods. Use the interface by creating another class that overrides those virtual methods.
使用纯虚拟方法创建一个类。使用接口创建另一个类来覆盖这些虚拟方法。
A pure virtual method is a class method that is defined as virtual and assigned to 0.
纯虚拟方法是一个类方法,它被定义为虚拟的,并被赋值为0。
class IDemo
{
public:
virtual ~IDemo() {}
virtual void OverrideMe() = 0;
};
class Child : public IDemo
{
public:
virtual void OverrideMe()
{
//do stuff
}
};
#3
125
The whole reason you have a special Interface type-category in addition to abstract base classes in C#/Java is because C#/Java do not support multiple inheritance.
在c# /Java中,除了抽象基类之外,还有一个特殊的接口类型,这是因为c# /Java不支持多重继承。
C++ supports multiple inheritance, and so a special type isn't needed. An abstract base class with no non-abstract (pure virtual) methods is functionally equivalent to a C#/Java interface.
c++支持多重继承,因此不需要特殊类型。一个抽象基类没有非抽象(纯虚拟)方法在功能上等同于c# /Java接口。
#4
42
There is no concept of "interface" per se in C++. AFAIK, interfaces were first introduced in Java to work around the lack of multiple inheritance. This concept has turned out to be quite useful, and the same effect can be achieved in C++ by using an abstract base class.
在c++中没有“接口”的概念。AFAIK接口最初是在Java中引入的,用于解决缺少多重继承的问题。这个概念已经被证明是非常有用的,并且通过使用一个抽象基类可以在c++中实现同样的效果。
An abstract base class is a class in which at least one member function (method in Java lingo) is a pure virtual function declared using the following syntax:
抽象基类是一个类,其中至少有一个成员函数(Java lingo中的方法)是使用以下语法声明的纯虚函数:
class A
{
virtual void foo() = 0;
};
An abstract base class cannot be instantiated, i. e. you cannot declare an object of class A. You can only derive classes from A, but any derived class that does not provide an implementation of foo()
will also be abstract. In order to stop being abstract, a derived class must provide implementations for all pure virtual functions it inherits.
抽象基类不能被实例化,即不能声明类的对象。您只能从A派生类,但是任何不提供foo()实现的派生类也将是抽象的。为了停止抽象,派生类必须为它所继承的所有纯虚函数提供实现。
Note that an abstract base class can be more than an interface, because it can contain data members and member functions that are not pure virtual. An equivalent of an interface would be an abstract base class without any data with only pure virtual functions.
注意,抽象基类可以不仅仅是一个接口,因为它可以包含非纯虚拟的数据成员和成员函数。与接口等价的是一个抽象基类,没有任何纯虚函数的数据。
And, as Mark Ransom pointed out, an abstract base class should provide a virtual destructor, just like any base class, for that matter.
而且,正如Mark Ransom所指出的,抽象基类应该提供一个虚拟析构函数,就像任何基类一样。
#5
38
As far I could test, it is very important to add the virtual destructor. I'm using objects created with new
and destroyed with delete
.
我可以测试,添加虚拟析构函数非常重要。我使用的对象是用new创建的,并且用delete来销毁。
If you do not add the virtual destructor in the interface, then the destructor of the inherited class is not called.
如果在接口中不添加虚拟析构函数,则不调用继承类的析构函数。
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;
}
If you run the previous code without virtual ~IBase() {};
, you will see that the destructor Tester::~Tester()
is never called.
如果在没有虚拟~IBase(){}的情况下运行前面的代码,您将看到析构测试器::~Tester()从未被调用。
#6
31
My answer is basically the same as the others but I think there are two other important things to do:
我的回答基本上和其他人一样,但我认为还有两件重要的事情要做:
-
Declare a virtual destructor in your interface or make a protected non-virtual one to avoid undefined behaviours if someone tries to delete an object of type
IDemo
.在您的接口中声明一个虚拟析构函数,或者在试图删除类型IDemo对象的情况下,创建一个受保护的非虚拟的,以避免未定义的行为。
-
Use virtual inheritance to avoid problems whith multiple inheritance. (There is more often multiple inheritance when we use interfaces.)
使用虚拟继承避免出现多重继承问题。(当我们使用接口时,更多的是多重继承。)
And like other answers:
就像其他的答案:
- Make a class with pure virtual methods.
- 使用纯虚拟方法创建一个类。
-
Use the interface by creating another class that overrides those virtual methods.
使用接口创建另一个类来覆盖这些虚拟方法。
class IDemo { public: virtual void OverrideMe() = 0; virtual ~IDemo() {} }
Or
或
class IDemo { public: virtual void OverrideMe() = 0; protected: ~IDemo() {} }
And
和
class Child : virtual public IDemo { public: virtual void OverrideMe() { //do stuff } }
#7
9
All good answers above. One extra thing you should keep in mind - you can also have a pure virtual destructor. The only difference is that you still need to implement it.
所有好的答案。还有一件事你应该记住——你也可以有一个纯粹的虚拟析构函数。唯一的区别是,您仍然需要实现它。
Confused?
困惑吗?
--- header file ----
class foo {
public:
foo() {;}
virtual ~foo() = 0;
virtual bool overrideMe() {return false;}
};
---- source ----
foo::~foo()
{
}
The main reason you'd want to do this is if you want to provide interface methods, as I have, but make overriding them optional.
您希望这样做的主要原因是,如果您想要提供接口方法,就像我所做的那样,但是要将它们重写为可选的。
To make the class an interface class requires a pure virtual method, but all of your virtual methods have default implementations, so the only method left to make pure virtual is the destructor.
为了使类成为一个接口类,需要一个纯虚拟的方法,但是所有的虚拟方法都有默认的实现,所以剩下的唯一的方法使纯虚拟成为析构函数。
Reimplementing a destructor in the derived class is no big deal at all - I always reimplement a destructor, virtual or not, in my derived classes.
在派生类中重新实现析构函数并不是什么大不了的事情——在我的派生类中,我总是重新实现析构函数。
#8
8
In C++11 you can easily avoid inheritance altogether:
在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;
// ...
};
In this case, an Interface has reference semantics, i.e. you have to make sure that the object outlives the interface (it is also possible to make interfaces with value semantics).
在这种情况下,接口具有引用语义,即您必须确保对象超出接口(也可以使用值语义进行接口)。
These type of interfaces have their pros and cons:
这些类型的接口有其优点和缺点:
- They require more memory than inheritance based polymorphism.
- 它们比基于继承的多态性需要更多的内存。
- They are in general faster than inheritance based polymorphism.
- 它们通常比基于继承的多态性更快。
- In those cases in which you know the final type, they are much faster! (some compilers like gcc and clang perform more optimizations in types that do not have/inherit from types with virtual functions).
- 在那些你知道最终类型的情况下,它们要快得多!(一些编译器,如gcc和clang,在不具有/从虚拟函数类型继承的类型中执行更多的优化)。
Finally, inheritance is the root of all evil in complex software design. In Sean Parent's Value Semantics and Concepts-based Polymorphism (highly recommended, better versions of this technique are explained there) the following case is studied:
最后,继承是复杂软件设计中所有罪恶的根源。在Sean Parent的值语义和基于概念的多态性(强烈推荐的,该技术的更好版本)中,我们研究了以下案例:
Say I have an application in which I deal with my shapes polymorphically using the MyShape
interface:
假设我有一个应用程序,我用MyShape接口来处理我的形状。
struct MyShape { virtual void my_draw() = 0; };
struct Circle : MyShape { void my_draw() { /* ... */ } };
// more shapes: e.g. triangle
In your application, you do the same with different shapes using the YourShape
interface:
在您的应用程序中,您使用您的形状接口对不同的形状进行相同的操作:
struct YourShape { virtual void your_draw() = 0; };
struct Square : YourShape { void your_draw() { /* ... */ } };
/// some more shapes here...
Now say you want to use some of the shapes that I've developed in your application. Conceptually, our shapes have the same interface, but to make my shapes work in your application you would need to extend my shapes as follows:
现在,假设您想要使用我在您的应用程序中开发的一些形状。从概念上讲,我们的形状具有相同的接口,但是要使我的形状在您的应用程序中工作,您需要扩展我的形状如下:
struct Circle : MyShape, YourShape {
void my_draw() { /*stays the same*/ };
void your_draw() { my_draw(); }
};
First, modifying my shapes might not be possible at all. Furthermore, multiple inheritance leads the road to spaghetti code (imagine a third project comes in that is using the TheirShape
interface... what happens if they also call their draw function my_draw
?).
首先,修改我的形状可能根本不可能。此外,多重继承导致了通往意大利面代码的道路(设想第三个项目使用了他们的形状接口……)如果他们也调用他们的绘制函数my_draw会发生什么?
Update: There are a couple of new references about non-inheritance based polymorphism:
更新:关于基于非继承的多态性有一些新的引用:
- Sean Parent's Inheritance is the base class of evil talk.
- 父母的继承是邪恶言论的基础。
- Sean Parent's Value-semantics and concept-based polymorphism talk.
- Sean Parent的价值语义和基于概念的多态对话。
- Pyry Jahkola's Inheritance free polymorphism talk and the poly library docs.
- Pyry Jahkola的继承*多态性谈话和保利图书馆文档。
- Zach Laine's Pragmatic Type Erasure: Solving OOP Problems with an Elegant Design Pattern talk.
- Zach Laine的实用型擦除:用优雅的设计模式谈话解决OOP问题。
- Andrzej's C++ blog - Type Erasure parts i, ii, iii, and iv.
- Andrzej的c++博客-类型删除部分i, ii, iii,和iv。
- Runtime Polymorphic Generic Programming—Mixing Objects and Concepts in ConceptC++
- 运行时多态性通用编程混合对象和概念在概念++。
- Boost.TypeErasure docs
- 提振。TypeErasure文档
- Adobe Poly docs
- Adobe保利文档
- Boost.Any, std::any proposal (revision 3), Boost.Spirit::hold_any.
- 提振。任何建议(修订3),Boost.Spirit::hold_any。
#9
7
If you're using Microsoft's C++ compiler, then you could do the following:
如果你使用的是微软的c++编译器,那么你可以这样做:
struct __declspec(novtable) IFoo
{
virtual void Bar() = 0;
};
class Child : public IFoo
{
public:
virtual void Bar() override { /* Do Something */ }
}
I like this approach because it results in a lot smaller interface code and the generated code size can be significantly smaller. The use of novtable removes all reference to the vtable pointer in that class, so you can never instantiate it directly. See the documentation here - novtable.
我喜欢这种方法,因为它会导致更小的接口代码,生成的代码大小可能会小得多。novtable的使用删除了该类中vtable指针的所有引用,因此您不能直接实例化它。参见这里的文档——novtable。
#10
4
A little addition to what's written up there:
上面写着:
First, make sure your destructor is also pure virtual
首先,确保您的析构函数也是纯虚拟的。
Second, you may want to inherit virtually (rather than normally) when you do implement, just for good measures.
其次,当您执行时,您可能想要继承(而不是通常),只是为了更好的度量。
#11
4
You can also consider contract classes implemented with the NVI (Non Virtual Interface Pattern). For instance:
您还可以考虑使用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.
};
#12
1
I'm still new in C++ development. I started with Visual Studio (VS).
我仍然是c++开发的新手。我从Visual Studio (VS)开始。
Yet, no one seems to mentioned the __interface
in VS (.NET). I am not very sure if this is a good way to declare an interface. But it seems to provide an additional enforcement (mentioned in the documents). Such that you don't have to explicitly specify the virtual TYPE Method() = 0;
, since it will be automatically converted.
然而,似乎没有人提到VS(。net)中的__interface。我不确定这是否是声明接口的好方法。但它似乎提供了额外的执行(在文件中提到)。这样您就不必显式地指定虚拟类型方法()= 0,因为它将被自动转换。
__interface IMyInterface {
HRESULT CommitX();
HRESULT get_X(BSTR* pbstrName);
};
However, I don't use it because I am concern about the cross platform compilation compatibility, since it only available under .NET.
但是,我不使用它,因为我担心跨平台的编译兼容性,因为它只在。net下可用。
If anyone do have anything interesting about it, please share. :-)
如果有人对此有兴趣,请分享。:-)
Thanks.
谢谢。
#13
1
Here is the definition of abstract class
in c++ standard
这是c++标准中抽象类的定义。
n4687
n4687
13.4.2
13.4.2
An abstract class is a class that can be used only as a base class of some other class; no objects of an abstract class can be created except as subobjects of a class derived from it. A class is abstract if it has at least one pure virtual function.
抽象类是一个类,它只能用作其他类的基类;抽象类的任何对象都不能创建,除了从它派生的类的子对象之外。一个类是抽象的,如果它至少有一个纯虚函数。
#14
0
While it's true that virtual
is the de-facto standard to define an interface, let's not forget about the classic C-like pattern, which comes with a constructor in C++:
虽然虚拟是定义接口的实际标准,但我们不要忘记了经典的C-like模式,它使用c++的构造函数:
struct IButton
{
void (*click)(); // might be std::function(void()) if you prefer
IButton( void (*click_)() )
: click(click_)
{
}
};
// call as:
// (button.*click)();
This has the advantage that you can re-bind events runtime without having to construct your class again (as C++ does not have a syntax for changing polymorphic types, this is a workaround for chameleon classes).
这样做的好处是,您可以重新绑定事件运行时,而不必重新构造类(因为c++没有用于改变多态类型的语法,这是变色龙类的一个变通方法)。
Tips:
小贴士:
- You might inherit from this as a base class (both virtual and non-virtual are permitted) and fill
click
in your descendant's constructor. - 您可以将其作为基类继承(允许虚拟和非虚拟),并在后代的构造函数中填充。
- You might have the function pointer as a
protected
member and have apublic
reference and/or getter. - 您可能拥有作为受保护成员的函数指针,并具有公共引用和/或getter。
- As mentioned above, this allows you to switch the implementation in runtime. Thus it's a way to manage state as well. Depending on the number of
if
s vs. state changes in your code, this might be faster thanswitch()
es orif
s (turnaround is expected around 3-4if
s, but always measure first. - 如上所述,这允许您在运行时切换实现。因此,这也是管理国家的一种方式。根据您的代码中ifs与状态更改的数量,这可能比switch()es或ifs要快(预期在3-4个ifs之间实现逆转,但总是先测量。
- If you choose
std::function<>
over function pointers, you might be able to manage all your object data withinIBase
. From this point, you can have value schematics forIBase
(e.g.,std::vector<IBase>
will work). Note that this might be slower depending on your compiler and STL code; also that current implementations ofstd::function<>
tend to have an overhead when compared to function pointers or even virtual functions (this might change in the future). -
如果您选择std::函数<>而不是函数指针,那么您可以在IBase中管理您的所有对象数据。从这一点上,您可以为IBase(例如,std::vector
将工作)的值示意图。注意,根据编译器和STL代码,这可能会慢一些;另外,std的当前实现::函数<>在与函数指针或甚至虚拟函数(可能在将来发生变化)相比,往往有一个开销。
#15
-1
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;
}
Result: Rectangle area: 35 Triangle area: 17
结果:矩形面积:35个三角形面积:17。
We have seen how an abstract class defined an interface in terms of getArea() and two other classes implemented same function but with different algorithm to calculate the area specific to the shape.
我们已经看到了一个抽象类如何用getArea()来定义一个接口,另外两个类实现了相同的功能,但是使用不同的算法来计算特定于形状的区域。
#1
614
To expand on the answer by bradtgmurray, you may want to make one exception to the pure virtual method list of your interface by adding a virtual destructor. This allows you to pass pointer ownership to another party without exposing the concrete derived class. The destructor doesn't have to do anything, because the interface doesn't have any concrete members. It might seem contradictory to define a function as both virtual and inline, but trust me - it isn't.
要扩展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
}
};
You don't have to include a body for the virtual destructor - it turns out some compilers have trouble optimizing an empty destructor and you're better off using the default.
您不需要为虚拟析构函数包含一个主体——它发现一些编译器在优化空析构函数时遇到了麻烦,而您最好使用缺省值。
#2
224
Make a class with pure virtual methods. Use the interface by creating another class that overrides those virtual methods.
使用纯虚拟方法创建一个类。使用接口创建另一个类来覆盖这些虚拟方法。
A pure virtual method is a class method that is defined as virtual and assigned to 0.
纯虚拟方法是一个类方法,它被定义为虚拟的,并被赋值为0。
class IDemo
{
public:
virtual ~IDemo() {}
virtual void OverrideMe() = 0;
};
class Child : public IDemo
{
public:
virtual void OverrideMe()
{
//do stuff
}
};
#3
125
The whole reason you have a special Interface type-category in addition to abstract base classes in C#/Java is because C#/Java do not support multiple inheritance.
在c# /Java中,除了抽象基类之外,还有一个特殊的接口类型,这是因为c# /Java不支持多重继承。
C++ supports multiple inheritance, and so a special type isn't needed. An abstract base class with no non-abstract (pure virtual) methods is functionally equivalent to a C#/Java interface.
c++支持多重继承,因此不需要特殊类型。一个抽象基类没有非抽象(纯虚拟)方法在功能上等同于c# /Java接口。
#4
42
There is no concept of "interface" per se in C++. AFAIK, interfaces were first introduced in Java to work around the lack of multiple inheritance. This concept has turned out to be quite useful, and the same effect can be achieved in C++ by using an abstract base class.
在c++中没有“接口”的概念。AFAIK接口最初是在Java中引入的,用于解决缺少多重继承的问题。这个概念已经被证明是非常有用的,并且通过使用一个抽象基类可以在c++中实现同样的效果。
An abstract base class is a class in which at least one member function (method in Java lingo) is a pure virtual function declared using the following syntax:
抽象基类是一个类,其中至少有一个成员函数(Java lingo中的方法)是使用以下语法声明的纯虚函数:
class A
{
virtual void foo() = 0;
};
An abstract base class cannot be instantiated, i. e. you cannot declare an object of class A. You can only derive classes from A, but any derived class that does not provide an implementation of foo()
will also be abstract. In order to stop being abstract, a derived class must provide implementations for all pure virtual functions it inherits.
抽象基类不能被实例化,即不能声明类的对象。您只能从A派生类,但是任何不提供foo()实现的派生类也将是抽象的。为了停止抽象,派生类必须为它所继承的所有纯虚函数提供实现。
Note that an abstract base class can be more than an interface, because it can contain data members and member functions that are not pure virtual. An equivalent of an interface would be an abstract base class without any data with only pure virtual functions.
注意,抽象基类可以不仅仅是一个接口,因为它可以包含非纯虚拟的数据成员和成员函数。与接口等价的是一个抽象基类,没有任何纯虚函数的数据。
And, as Mark Ransom pointed out, an abstract base class should provide a virtual destructor, just like any base class, for that matter.
而且,正如Mark Ransom所指出的,抽象基类应该提供一个虚拟析构函数,就像任何基类一样。
#5
38
As far I could test, it is very important to add the virtual destructor. I'm using objects created with new
and destroyed with delete
.
我可以测试,添加虚拟析构函数非常重要。我使用的对象是用new创建的,并且用delete来销毁。
If you do not add the virtual destructor in the interface, then the destructor of the inherited class is not called.
如果在接口中不添加虚拟析构函数,则不调用继承类的析构函数。
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;
}
If you run the previous code without virtual ~IBase() {};
, you will see that the destructor Tester::~Tester()
is never called.
如果在没有虚拟~IBase(){}的情况下运行前面的代码,您将看到析构测试器::~Tester()从未被调用。
#6
31
My answer is basically the same as the others but I think there are two other important things to do:
我的回答基本上和其他人一样,但我认为还有两件重要的事情要做:
-
Declare a virtual destructor in your interface or make a protected non-virtual one to avoid undefined behaviours if someone tries to delete an object of type
IDemo
.在您的接口中声明一个虚拟析构函数,或者在试图删除类型IDemo对象的情况下,创建一个受保护的非虚拟的,以避免未定义的行为。
-
Use virtual inheritance to avoid problems whith multiple inheritance. (There is more often multiple inheritance when we use interfaces.)
使用虚拟继承避免出现多重继承问题。(当我们使用接口时,更多的是多重继承。)
And like other answers:
就像其他的答案:
- Make a class with pure virtual methods.
- 使用纯虚拟方法创建一个类。
-
Use the interface by creating another class that overrides those virtual methods.
使用接口创建另一个类来覆盖这些虚拟方法。
class IDemo { public: virtual void OverrideMe() = 0; virtual ~IDemo() {} }
Or
或
class IDemo { public: virtual void OverrideMe() = 0; protected: ~IDemo() {} }
And
和
class Child : virtual public IDemo { public: virtual void OverrideMe() { //do stuff } }
#7
9
All good answers above. One extra thing you should keep in mind - you can also have a pure virtual destructor. The only difference is that you still need to implement it.
所有好的答案。还有一件事你应该记住——你也可以有一个纯粹的虚拟析构函数。唯一的区别是,您仍然需要实现它。
Confused?
困惑吗?
--- header file ----
class foo {
public:
foo() {;}
virtual ~foo() = 0;
virtual bool overrideMe() {return false;}
};
---- source ----
foo::~foo()
{
}
The main reason you'd want to do this is if you want to provide interface methods, as I have, but make overriding them optional.
您希望这样做的主要原因是,如果您想要提供接口方法,就像我所做的那样,但是要将它们重写为可选的。
To make the class an interface class requires a pure virtual method, but all of your virtual methods have default implementations, so the only method left to make pure virtual is the destructor.
为了使类成为一个接口类,需要一个纯虚拟的方法,但是所有的虚拟方法都有默认的实现,所以剩下的唯一的方法使纯虚拟成为析构函数。
Reimplementing a destructor in the derived class is no big deal at all - I always reimplement a destructor, virtual or not, in my derived classes.
在派生类中重新实现析构函数并不是什么大不了的事情——在我的派生类中,我总是重新实现析构函数。
#8
8
In C++11 you can easily avoid inheritance altogether:
在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;
// ...
};
In this case, an Interface has reference semantics, i.e. you have to make sure that the object outlives the interface (it is also possible to make interfaces with value semantics).
在这种情况下,接口具有引用语义,即您必须确保对象超出接口(也可以使用值语义进行接口)。
These type of interfaces have their pros and cons:
这些类型的接口有其优点和缺点:
- They require more memory than inheritance based polymorphism.
- 它们比基于继承的多态性需要更多的内存。
- They are in general faster than inheritance based polymorphism.
- 它们通常比基于继承的多态性更快。
- In those cases in which you know the final type, they are much faster! (some compilers like gcc and clang perform more optimizations in types that do not have/inherit from types with virtual functions).
- 在那些你知道最终类型的情况下,它们要快得多!(一些编译器,如gcc和clang,在不具有/从虚拟函数类型继承的类型中执行更多的优化)。
Finally, inheritance is the root of all evil in complex software design. In Sean Parent's Value Semantics and Concepts-based Polymorphism (highly recommended, better versions of this technique are explained there) the following case is studied:
最后,继承是复杂软件设计中所有罪恶的根源。在Sean Parent的值语义和基于概念的多态性(强烈推荐的,该技术的更好版本)中,我们研究了以下案例:
Say I have an application in which I deal with my shapes polymorphically using the MyShape
interface:
假设我有一个应用程序,我用MyShape接口来处理我的形状。
struct MyShape { virtual void my_draw() = 0; };
struct Circle : MyShape { void my_draw() { /* ... */ } };
// more shapes: e.g. triangle
In your application, you do the same with different shapes using the YourShape
interface:
在您的应用程序中,您使用您的形状接口对不同的形状进行相同的操作:
struct YourShape { virtual void your_draw() = 0; };
struct Square : YourShape { void your_draw() { /* ... */ } };
/// some more shapes here...
Now say you want to use some of the shapes that I've developed in your application. Conceptually, our shapes have the same interface, but to make my shapes work in your application you would need to extend my shapes as follows:
现在,假设您想要使用我在您的应用程序中开发的一些形状。从概念上讲,我们的形状具有相同的接口,但是要使我的形状在您的应用程序中工作,您需要扩展我的形状如下:
struct Circle : MyShape, YourShape {
void my_draw() { /*stays the same*/ };
void your_draw() { my_draw(); }
};
First, modifying my shapes might not be possible at all. Furthermore, multiple inheritance leads the road to spaghetti code (imagine a third project comes in that is using the TheirShape
interface... what happens if they also call their draw function my_draw
?).
首先,修改我的形状可能根本不可能。此外,多重继承导致了通往意大利面代码的道路(设想第三个项目使用了他们的形状接口……)如果他们也调用他们的绘制函数my_draw会发生什么?
Update: There are a couple of new references about non-inheritance based polymorphism:
更新:关于基于非继承的多态性有一些新的引用:
- Sean Parent's Inheritance is the base class of evil talk.
- 父母的继承是邪恶言论的基础。
- Sean Parent's Value-semantics and concept-based polymorphism talk.
- Sean Parent的价值语义和基于概念的多态对话。
- Pyry Jahkola's Inheritance free polymorphism talk and the poly library docs.
- Pyry Jahkola的继承*多态性谈话和保利图书馆文档。
- Zach Laine's Pragmatic Type Erasure: Solving OOP Problems with an Elegant Design Pattern talk.
- Zach Laine的实用型擦除:用优雅的设计模式谈话解决OOP问题。
- Andrzej's C++ blog - Type Erasure parts i, ii, iii, and iv.
- Andrzej的c++博客-类型删除部分i, ii, iii,和iv。
- Runtime Polymorphic Generic Programming—Mixing Objects and Concepts in ConceptC++
- 运行时多态性通用编程混合对象和概念在概念++。
- Boost.TypeErasure docs
- 提振。TypeErasure文档
- Adobe Poly docs
- Adobe保利文档
- Boost.Any, std::any proposal (revision 3), Boost.Spirit::hold_any.
- 提振。任何建议(修订3),Boost.Spirit::hold_any。
#9
7
If you're using Microsoft's C++ compiler, then you could do the following:
如果你使用的是微软的c++编译器,那么你可以这样做:
struct __declspec(novtable) IFoo
{
virtual void Bar() = 0;
};
class Child : public IFoo
{
public:
virtual void Bar() override { /* Do Something */ }
}
I like this approach because it results in a lot smaller interface code and the generated code size can be significantly smaller. The use of novtable removes all reference to the vtable pointer in that class, so you can never instantiate it directly. See the documentation here - novtable.
我喜欢这种方法,因为它会导致更小的接口代码,生成的代码大小可能会小得多。novtable的使用删除了该类中vtable指针的所有引用,因此您不能直接实例化它。参见这里的文档——novtable。
#10
4
A little addition to what's written up there:
上面写着:
First, make sure your destructor is also pure virtual
首先,确保您的析构函数也是纯虚拟的。
Second, you may want to inherit virtually (rather than normally) when you do implement, just for good measures.
其次,当您执行时,您可能想要继承(而不是通常),只是为了更好的度量。
#11
4
You can also consider contract classes implemented with the NVI (Non Virtual Interface Pattern). For instance:
您还可以考虑使用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.
};
#12
1
I'm still new in C++ development. I started with Visual Studio (VS).
我仍然是c++开发的新手。我从Visual Studio (VS)开始。
Yet, no one seems to mentioned the __interface
in VS (.NET). I am not very sure if this is a good way to declare an interface. But it seems to provide an additional enforcement (mentioned in the documents). Such that you don't have to explicitly specify the virtual TYPE Method() = 0;
, since it will be automatically converted.
然而,似乎没有人提到VS(。net)中的__interface。我不确定这是否是声明接口的好方法。但它似乎提供了额外的执行(在文件中提到)。这样您就不必显式地指定虚拟类型方法()= 0,因为它将被自动转换。
__interface IMyInterface {
HRESULT CommitX();
HRESULT get_X(BSTR* pbstrName);
};
However, I don't use it because I am concern about the cross platform compilation compatibility, since it only available under .NET.
但是,我不使用它,因为我担心跨平台的编译兼容性,因为它只在。net下可用。
If anyone do have anything interesting about it, please share. :-)
如果有人对此有兴趣,请分享。:-)
Thanks.
谢谢。
#13
1
Here is the definition of abstract class
in c++ standard
这是c++标准中抽象类的定义。
n4687
n4687
13.4.2
13.4.2
An abstract class is a class that can be used only as a base class of some other class; no objects of an abstract class can be created except as subobjects of a class derived from it. A class is abstract if it has at least one pure virtual function.
抽象类是一个类,它只能用作其他类的基类;抽象类的任何对象都不能创建,除了从它派生的类的子对象之外。一个类是抽象的,如果它至少有一个纯虚函数。
#14
0
While it's true that virtual
is the de-facto standard to define an interface, let's not forget about the classic C-like pattern, which comes with a constructor in C++:
虽然虚拟是定义接口的实际标准,但我们不要忘记了经典的C-like模式,它使用c++的构造函数:
struct IButton
{
void (*click)(); // might be std::function(void()) if you prefer
IButton( void (*click_)() )
: click(click_)
{
}
};
// call as:
// (button.*click)();
This has the advantage that you can re-bind events runtime without having to construct your class again (as C++ does not have a syntax for changing polymorphic types, this is a workaround for chameleon classes).
这样做的好处是,您可以重新绑定事件运行时,而不必重新构造类(因为c++没有用于改变多态类型的语法,这是变色龙类的一个变通方法)。
Tips:
小贴士:
- You might inherit from this as a base class (both virtual and non-virtual are permitted) and fill
click
in your descendant's constructor. - 您可以将其作为基类继承(允许虚拟和非虚拟),并在后代的构造函数中填充。
- You might have the function pointer as a
protected
member and have apublic
reference and/or getter. - 您可能拥有作为受保护成员的函数指针,并具有公共引用和/或getter。
- As mentioned above, this allows you to switch the implementation in runtime. Thus it's a way to manage state as well. Depending on the number of
if
s vs. state changes in your code, this might be faster thanswitch()
es orif
s (turnaround is expected around 3-4if
s, but always measure first. - 如上所述,这允许您在运行时切换实现。因此,这也是管理国家的一种方式。根据您的代码中ifs与状态更改的数量,这可能比switch()es或ifs要快(预期在3-4个ifs之间实现逆转,但总是先测量。
- If you choose
std::function<>
over function pointers, you might be able to manage all your object data withinIBase
. From this point, you can have value schematics forIBase
(e.g.,std::vector<IBase>
will work). Note that this might be slower depending on your compiler and STL code; also that current implementations ofstd::function<>
tend to have an overhead when compared to function pointers or even virtual functions (this might change in the future). -
如果您选择std::函数<>而不是函数指针,那么您可以在IBase中管理您的所有对象数据。从这一点上,您可以为IBase(例如,std::vector
将工作)的值示意图。注意,根据编译器和STL代码,这可能会慢一些;另外,std的当前实现::函数<>在与函数指针或甚至虚拟函数(可能在将来发生变化)相比,往往有一个开销。
#15
-1
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;
}
Result: Rectangle area: 35 Triangle area: 17
结果:矩形面积:35个三角形面积:17。
We have seen how an abstract class defined an interface in terms of getArea() and two other classes implemented same function but with different algorithm to calculate the area specific to the shape.
我们已经看到了一个抽象类如何用getArea()来定义一个接口,另外两个类实现了相同的功能,但是使用不同的算法来计算特定于形状的区域。