如何设置表示接口的类?这只是一个抽象基类吗?
当前回答
除了上面写的内容,还有一点补充:
首先,确保析构函数也是纯虚拟的
第二,您可能希望在执行时实际上继承(而不是正常继承),只是为了获得好的度量。
其他回答
为了扩展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
}
};
您不必为虚拟析构函数包含一个主体——事实证明,某些编译器在优化空析构函数时遇到了问题,最好使用默认值。
如果您只需要接口的静态绑定(没有虚拟的,没有接口类型本身的实例,接口仅作为指南):
#include <iostream>
#include <string>
// Static binding interface
// Notice: instantiation of this interface should be usefuless and forbidden.
class IBase {
protected:
IBase() = default;
~IBase() = default;
public:
// Methods that must be implemented by the derived class
void behaviorA();
void behaviorB();
void behaviorC() {
std::cout << "This is an interface default implementation of bC().\n";
};
};
class CCom : public IBase {
std::string name_;
public:
void behaviorA() { std::cout << "CCom bA called.\n"; };
};
class CDept : public IBase {
int ele_;
public:
void behaviorB() { std::cout << "CDept bB called.\n"; };
void behaviorC() {
// Overwrite the interface default implementation
std::cout << "CDept bC called.\n";
IBase::behaviorC();
};
};
int main(void) {
// Forbid the instantiation of the interface type itself.
// GCC error: ‘constexpr IBase::IBase()’ is protected within this context
// IBase o;
CCom acom;
// If you want to use these interface methods, you need to implement them in
// your derived class. This is controled by the interface definition.
acom.behaviorA();
// ld: undefined reference to `IBase::behaviorB()'
// acom.behaviorB();
acom.behaviorC();
CDept adept;
// adept.behaviorA();
adept.behaviorB();
adept.behaviorC();
// adept.IBase::behaviorC();
}
虽然虚拟是定义接口的事实标准,但我们不要忘记经典的类C模式,它在C++中带有构造函数:
struct IButton
{
void (*click)(); // might be std::function(void()) if you prefer
IButton( void (*click_)() )
: click(click_)
{
}
};
// call as:
// (button.*click)();
这样做的优点是,您可以在运行时重新绑定事件,而无需再次构造类(因为C++没有用于更改多态类型的语法,这是变色龙类的一种变通方法)。
提示:
您可以将其作为基类继承(允许虚拟和非虚拟),并在后代的构造函数中填充单击。您可以将函数指针作为受保护的成员,并具有公共引用和/或getter。如上所述,这允许您在运行时切换实现。因此,这也是一种管理状态的方法。根据代码中ifs与状态变化的数量,这可能比switch()es或ifs更快(预计周转时间在3-4个ifs左右,但始终要先测量。如果在函数指针上选择std::function<>,则可能能够管理IBase中的所有对象数据。从这一点开始,您可以获得IBase的值示意图(例如,std::vector<IBase>将起作用)。注意,根据编译器和STL代码的不同,这可能会更慢;此外,与函数指针甚至虚拟函数相比,std::function<>的当前实现往往会有开销(这在将来可能会改变)。
以上都是好答案。还有一件事你应该记住——你也可以有一个纯粹的虚拟析构函数。唯一的区别是你仍然需要实现它。
困惑的
--- header file ----
class foo {
public:
foo() {;}
virtual ~foo() = 0;
virtual bool overrideMe() {return false;}
};
---- source ----
foo::~foo()
{
}
你想这样做的主要原因是,如果你想提供接口方法,就像我所说的那样,但让重写它们成为可选的。
要使类成为接口类,需要一个纯虚方法,但所有的虚方法都有默认实现,因此唯一剩下的方法就是析构函数。
在派生类中重新实现析构函数根本没什么大不了的——我总是在派生类里重新实现一个析构函数,不管是虚拟的还是非虚拟的。
除了C#/Java中的抽象基类之外,您还有一个特殊的接口类型类别,这是因为C#/Java不支持多重继承。
C++支持多重继承,因此不需要特殊类型。没有非抽象(纯虚拟)方法的抽象基类在功能上等同于C#/Java接口。
推荐文章
- 目标文件中无法解析的外部符号
- Lambda捕获作为const引用?
- 如何从c风格数组初始化std::vector ?
- 是std::vector复制对象push_back?
- 为什么在c++ 11中使用非成员begin和end函数?
- 如何通过参数和重定向stdin从一个文件到程序运行在gdb?
- 为什么两个不同的概念都叫“堆”?
- 为什么这个结合赋值和相等检查的if语句返回true?
- cplusplus.com给出的错误、误解或坏建议是什么?
- 找出质数最快的算法是什么?
- c++枚举类可以有方法吗?
- 格式化IO函数(*printf / *scanf)中的转换说明符%i和%d之间的区别是什么?
- 将析构函数设为私有有什么用?
- main()中的Return语句vs exit()
- 为什么c#不提供c++风格的'friend'关键字?