如何设置表示接口的类?这只是一个抽象基类吗?
当前回答
为了扩展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
}
};
您不必为虚拟析构函数包含一个主体——事实证明,某些编译器在优化空析构函数时遇到了问题,最好使用默认值。
其他回答
除了C#/Java中的抽象基类之外,您还有一个特殊的接口类型类别,这是因为C#/Java不支持多重继承。
C++支持多重继承,因此不需要特殊类型。没有非抽象(纯虚拟)方法的抽象基类在功能上等同于C#/Java接口。
为了扩展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
}
};
您不必为虚拟析构函数包含一个主体——事实证明,某些编译器在优化空析构函数时遇到了问题,最好使用默认值。
在C++20中,可以使用概念而不是类。它比继承更有效率。
template <class T>
concept MyInterface = requires (T t) {
{ t.interfaceMethod() };
};
class Implementation {
public:
void interfaceMethod();
};
static_assert(MyInterface<Implementation>);
然后您可以在函数中使用它:
void myFunction(MyInterface auto& arg);
限制是不能在容器中使用它。
我的答案与其他人基本相同,但我认为还有两件重要的事情要做:
如果有人试图删除IDemo类型的对象,请在接口中声明一个虚拟析构函数或创建一个受保护的非虚拟析构器,以避免未定义的行为。使用虚拟继承来避免多重继承的问题。(当我们使用接口时,通常会有多重继承。)
和其他答案一样:
使用纯虚拟方法创建类。通过创建另一个重写这些虚拟方法的类来使用该接口。类IDemo{公众:虚空OverrideMe()=0;virtual~IDemo(){}}或类IDemo{公众:虚空OverrideMe()=0;受保护的:~IDemo(){}}和class Child:虚拟公共IDemo{公众:虚拟空OverrideMe(){//做一些事情}}
虽然虚拟是定义接口的事实标准,但我们不要忘记经典的类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<>的当前实现往往会有开销(这在将来可能会改变)。