你如何在 C++ 中声明一个接口?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/318064/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you declare an interface in C++?
提问by Aaron Fischer
How do I setup a class that represents an interface? Is this just an abstract base class?
如何设置代表接口的类?这只是一个抽象基类吗?
采纳答案by Mark Ransom
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.
您不必包含虚拟析构函数的主体 - 事实证明,某些编译器在优化空析构函数时遇到问题,您最好使用默认值。
回答by bradtgmurray
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
}
};
回答by Joel Coehoorn
The whole reason you have a special Interface type-category in addition to abstract base classes in C#/Javais 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 接口。
回答by Dima
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 术语中的方法)是使用以下语法声明的纯虚函数:
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 的对象。只能从 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 指出的那样,抽象基类应该提供一个虚拟析构函数,就像任何基类一样。
回答by Carlos C Soto
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.
如果在没有 的情况下运行前面的代码virtual ~IBase() {};
,您将看到Tester::~Tester()
从未调用析构函数。
回答by Rexxar
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
.Use virtual inheritance to avoid problems whith multiple inheritance. (There is more often multiple inheritance when we use interfaces.)
在您的接口中声明一个虚拟析构函数或创建一个受保护的非虚拟析构函数以避免在有人试图删除类型为 的对象时出现未定义的行为
IDemo
。使用虚拟继承来避免多重继承的问题。(当我们使用接口时,更常见的是多重继承。)
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 } }
- 使用纯虚方法创建一个类。
通过创建另一个覆盖这些虚拟方法的类来使用该接口。
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 } }
回答by gnzlbg
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 memorythan inheritance based polymorphism.
- They are in general fasterthan 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 的 Value Semantics and Concepts-based Polymorphism(强烈推荐,那里解释了该技术的更好版本)中,研究了以下案例:
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:
在您的应用程序中,您可以使用YourShape
界面对不同的形状执行相同的操作:
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
?).
首先,可能根本不可能修改我的形状。此外,多重继承导致通向意大利面条式代码的道路(想象一下,第三个项目正在使用TheirShape
接口......如果他们也调用他们的绘制函数会发生什么my_draw
?)。
Update: There are a couple of new references about non-inheritance based polymorphism:
更新:有一些关于基于非继承的多态性的新参考:
- Sean Parent's Inheritance is the base class of eviltalk.
- Sean Parent's Value-semantics and concept-based polymorphismtalk.
- Pyry Jahkola's Inheritance free polymorphismtalk and the poly library docs.
- Zach Laine's Pragmatic Type Erasure: Solving OOP Problems with an Elegant Design Patterntalk.
- Andrzej's C++ blog - Type Erasure parts i, ii, iii, and iv.
- Runtime Polymorphic Generic Programming—Mixing Objects and Concepts in ConceptC++
- Boost.TypeErasure docs
- Adobe Poly docs
- Boost.Any, std::any proposal (revision 3), Boost.Spirit::hold_any.
- Sean Parent's Inheritance 是恶言的基类。
- Sean Parent 的价值语义和基于概念的多态性演讲。
- Pyry Jahkola 的无继承多态性演讲和poly 库文档。
- Zach Laine 的Pragmatic Type Erasure: Solving OOP Problems with an Elegant Design Patterntalk。
- Andrzej 的 C++ 博客 - 类型擦除部分i、ii、iii和iv。
- 运行时多态泛型编程——在ConceptC++中混合对象和概念
- Boost.TypeErasure 文档
- Adobe Poly 文档
- Boost.Any、std::any 提案(修订版 3)、Boost.Spirit::hold_any。
回答by Rodyland
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.
在派生类中重新实现析构函数根本没有什么大不了的 - 我总是在派生类中重新实现析构函数,无论是否虚拟。
回答by Mark Ingram
If you're using Microsoft's C++ compiler, then you could do the following:
如果您使用的是 Microsoft 的 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。
回答by Uri
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.
其次,您可能希望在实施时虚拟继承(而不是通常),只是为了好的措施。