如何在 C++ 中声明接口?
如何设置代表接口的类? 这只是一个抽象基类吗?
How do I setup a class that represents an interface? Is this just an abstract base class?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
如何设置代表接口的类? 这只是一个抽象基类吗?
How do I setup a class that represents an interface? Is this just an abstract base class?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(17)
使用纯虚方法创建一个类。 通过创建另一个重写这些虚拟方法的类来使用该接口。
纯虚方法是定义为虚方法并赋值为 0 的类方法。
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.
要扩展 bradtgmurray 的答案,您可以想要通过添加虚拟析构函数来对接口的纯虚拟方法列表做出一个例外。 这允许您将指针所有权传递给另一方,而无需公开具体的派生类。 析构函数不必执行任何操作,因为该接口没有任何具体成员。 将函数定义为虚拟函数和内联函数似乎是矛盾的,但相信我 - 事实并非如此。
您不必包含虚拟析构函数的主体 - 事实证明,某些编译器在优化空析构函数时遇到困难,您最好使用默认值。
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.
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.
除了 C#/ 中的抽象基类之外,您还拥有特殊的 Interface 类型类别的全部原因Java是因为C#/Java不支持多重继承。
C++ 支持多重继承,因此不需要特殊类型。 没有非抽象(纯虚拟)方法的抽象基类在功能上等同于 C#/Java 接口。
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++ 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++ 本身没有“接口”的概念。 AFAIK,接口首先在 Java 中引入是为了解决多重继承的缺乏。 事实证明这个概念非常有用,在 C++ 中通过使用抽象基类也可以达到相同的效果。
抽象基类是这样一种类,其中至少有一个成员函数(Java 术语中的方法)是使用以下语法声明的纯虚函数:
抽象基类无法实例化,即不能声明类 A 的对象。只能从 A 派生类,但任何不提供 foo() 实现的派生类也将是抽象类。 为了不再是抽象的,派生类必须为其继承的所有纯虚函数提供实现。
请注意,抽象基类可以不仅仅是一个接口,因为它可以包含非纯虚的数据成员和成员函数。 接口的等效项是没有任何数据成员且仅包含纯虚函数的抽象基类。
而且,正如 Mark Ransom 指出的那样,抽象基类应该提供一个虚拟析构函数,就像任何基类一样。
考虑这个问题的一个好方法是继承接口与继承实现。 在 C++ 中,您可以同时继承接口和实现(公共继承),也可以仅继承实现(私有继承)。 在 Java 中,您可以选择仅继承接口,而不继承实现。
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.
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:
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.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 members, and containing 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.
A good way to think of this is in terms of inheriting an interface vs. inheriting an implementation. In C++ you can either inherit both interface and implementation together (public inheritance) or you can inherit only the implementation (private inheritance). In Java you have the option of inheriting just the interface, without an implementation.
据我测试,添加虚拟析构函数非常重要。 我使用的是用
new
创建并用delete
销毁的对象。如果接口中没有添加虚析构函数,那么继承类的析构函数就不会被调用。
如果您在没有
virtual ~IBase() {};
的情况下运行前面的代码,您将看到析构函数Tester::~Tester()
从未被调用。As far I could test, it is very important to add the virtual destructor. I'm using objects created with
new
and destroyed withdelete
.If you do not add the virtual destructor in the interface, then the destructor of the inherited class is not called.
If you run the previous code without
virtual ~IBase() {};
, you will see that the destructorTester::~Tester()
is never called.我的答案基本上与其他人相同,但我认为还有另外两件重要的事情要做:
在界面中声明一个虚拟析构函数或创建一个受保护的非虚拟析构函数,以避免在有人尝试删除对象时出现未定义的行为
IDemo
类型。使用虚拟继承来避免多重继承的问题。 (当我们使用接口时,更常见的是多重继承。)
并且像其他答案一样:
通过创建另一个覆盖这些虚拟方法的类来使用该接口。
或者
和
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.)
And like other answers:
Use the interface by creating another class that overrides those virtual methods.
Or
And
在 C++11 中,您可以轻松地完全避免继承:
在这种情况下,接口具有引用语义,即您必须确保对象比接口寿命更长(也可以创建具有值语义的接口)。
这些类型的接口各有优缺点:
最后,继承是复杂软件设计中的万恶之源。 在 Sean Parent 的价值语义和基于概念的多态性(强烈推荐,该技术的更好版本)在那里进行了解释)研究了以下案例:
假设我有一个应用程序,其中我使用 MyShape 接口以多态方式处理我的形状:
在您的应用程序中,您使用
对不同的形状执行相同的操作>YourShape
界面:现在假设您想使用我在您的应用程序中开发的一些形状。 从概念上讲,我们的形状具有相同的界面,但为了使我的形状在您的应用程序中工作,您需要按如下方式扩展我的形状:
首先,修改我的形状可能根本不可能。 此外,多重继承导致了意大利面条式代码的发展(想象一下第三个项目使用
TheirShape
接口......如果他们也调用其绘制函数my_draw
会发生什么?)。更新:有一些关于基于非继承的多态性的新参考文献:
In C++11 you can easily avoid inheritance altogether:
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:
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:
Say I have an application in which I deal with my shapes polymorphically using the
MyShape
interface:In your application, you do the same with different shapes using the
YourShape
interface: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:
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 functionmy_draw
?).Update: There are a couple of new references about non-inheritance based polymorphism:
对上面写的内容进行一点补充:
首先,确保您的析构函数也是纯虚拟的
。其次,您可能希望在实现时虚拟继承(而不是正常继承),只是为了采取良好的措施。
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.
在 C++20 中,您可以使用
概念
而不是类。它比继承更有效率。
然后你可以在函数中使用它:
限制是你不能在容器中使用它。
In C++20, you can use a
concept
instead of a class.It is more efficient than inheritance.
Then you can use it in a function:
The limitation is that you cannot use it in a container.
虽然
virtual
确实是定义接口的事实上的标准,但我们不要忘记经典的类似 C 的模式,它带有 C++ 中的构造函数:这样做的优点是您可以重新- 运行时绑定事件,无需再次构造类(因为 C++ 没有用于更改多态类型的语法,这是变色龙类的解决方法)。
提示:
click
。protected
成员,并具有public
引用和/或 getter。if
的数量与状态更改的数量,此可能比switch()
es 或if 更快
s(预计周转时间约为 3-4 个if
s,但始终先进行测量。std::function<>
而不是函数指针,则可能能够管理IBase
中的所有对象数据。从这一点开始,您就可以获得IBase
的值示意图(例如,)。 std::vector
可以工作)。 ; 与函数指针甚至虚函数相比,往往会产生开销(这在将来可能会改变)。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++: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).
Tips:
click
in your descendant's constructor.protected
member and have apublic
reference and/or getter.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.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).上面都是很好的答案。
您应该记住的另一件事 - 您还可以拥有纯虚拟析构函数。 唯一的区别是您仍然需要实现它。
使困惑?
您想要这样做的主要原因是,如果您想提供接口方法,就像我一样,但使重写它们成为可选的。
要使该类成为接口类,需要一个纯虚方法,但所有虚方法都有默认实现,因此剩下的唯一使纯虚方法是析构函数。
在派生类中重新实现析构函数根本不是什么大问题 - 我总是在派生类中重新实现析构函数,无论是否是虚拟的。
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?
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.
您还可以考虑使用 NVI(非虚拟接口模式)实现的合约类。 例如:
You can also consider contract classes implemented with the NVI (Non Virtual Interface Pattern). For instance:
如果您使用 Microsoft 的 C++ 编译器,那么您可以执行以下操作:
我喜欢这种方法,因为它会产生更小的接口代码,并且生成的代码大小可以明显更小。 使用 novtable 会删除对该类中 vtable 指针的所有引用,因此您永远无法直接实例化它。 请参阅此处的文档 - novtable。
If you're using Microsoft's C++ compiler, then you could do the following:
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.
我对 C++ 开发还是个新手。 我从 Visual Studio (VS) 开始。
然而,似乎没有人提到 VS (.NET) 中的
__interface
。 我不太非常确定这是否是声明接口的好方法。 但它似乎提供了额外的强制(在文档)。 这样您就不必显式指定virtual TYPE Method() = 0;
,因为它会自动转换。如果有人对此有什么有趣的事情,请分享。 :-)
谢谢。
I'm still new in C++ development. I started with 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 thevirtual TYPE Method() = 0;
, since it will be automatically converted.If anyone do have anything interesting about it, please share. :-)
Thanks.
中
抽象类
的定义这是c++标准n4687
13.4.2
Here is the definition of
abstract class
in c++ standardn4687
13.4.2
如果您只需要接口的静态绑定(没有虚拟,没有接口类型本身的实例,接口仅充当指南):
In case you only want static binding of an interface(no virtual, no instances of the interface type itself, interface only act as a guide):
结果:
矩形面积:35
三角形面积:17
我们已经看到抽象类如何根据 getArea() 定义接口,另外两个类实现相同的功能但使用不同的算法来计算特定于形状的面积。
Result:
Rectangle area: 35
Triangle area: 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.