如何在C++中模拟接口?

Ton*_*ony 38 c++ abstract-class interface naming-conventions

由于C++缺少interfaceJava和C#的特性,在C++类中模拟接口的首选方法是什么?我的猜测是抽象类的多重继承.在内存开销/性能方面有什么影响?这种模拟接口是否有任何命名约定,例如SerializableInterface

Bri*_*ndy 39

由于C++具有多个继承,这与C#和Java不同,是的,您可以创建一系列抽象类.

至于惯例,由你决定; 但是,我喜欢在班级名称之前加上我.

class IStringNotifier
{
public:
  virtual void sendMessage(std::string &strMessage) = 0;
  virtual ~IStringNotifier() { }
};
Run Code Online (Sandbox Code Playgroud)

在C#和Java之间的比较方面,性能无需担心.基本上你只需要为你的函数或vtable创建一个查找表,就像使用虚拟方法给出的任何类型的继承一样.

  • @Michael Aaron Safyan:感谢您的支持,也许如果您通过他们的回答来判断问题,而不是您对编码风格的偏好,那会更好吗? (9认同)
  • 如果要使用指向抽象类的指针删除对象,请使抽象类析构函数为virtual. (8认同)
  • +1为匈牙利表示法的人.(当然答案:-)) (3认同)

CB *_*ley 11

实际上没有必要"模拟"任何东西,因为不是C++缺少Java可以用接口做的任何事情.

从C++指针来看,Java在a interface和a 之间进行了"人为的"分析class.An interface只是其class所有方法都是抽象的,不能包含任何数据成员.

Java使得这种限制,因为它不允许不受限制的多重继承,但它允许一个classimplement多个接口.

在C++中,a class是a class,a interface是a class.extends通过公共继承implements实现,也通过公共继承实现.

从多个非接口类继承可能会导致额外的复杂性,但在某些情况下可能很有用.如果你自己只限于从最多一个非接口类和任意数量的完全抽象类继承类,那么你将不会遇到任何其他困难,而不是在Java中(除了其他C++/Java差异).

就内存和开销成本而言,如果要重新创建Java样式类层次结构,那么在任何情况下,您可能已经在类上支付了虚函数成本.鉴于您无论如何都在使用不同的运行时环境,在不同的继承模型的成本方面,两者之间的开销不会有任何根本的差异.


Ste*_*sop 7

"在内存开销/性能方面有什么影响?"

除了使用虚拟呼叫之外通常都没有,尽管标准在性能方面没有什么保证.

在内存开销方面,"空基类"优化显式允许编译器布局结构,以便添加没有数据成员的基类不会增加对象的大小.我认为你不太可能不必处理不能做到这一点的编译器,但我可能错了.

将第一个虚拟成员函数添加到类通常会增加对象的指针大小,与它们没有虚拟成员函数的情况相比.添加更多虚拟成员函数没有任何其他区别.添加虚拟基类可能会产生进一步的不同,但您不需要那些正在谈论的内容.

使用虚拟成员函数添加多个基类可能意味着实际上只获得一次空基类优化,因为在典型的实现中,对象将需要多个vtable指针.因此,如果每个类需要多个接口,则可能会增加对象的大小.

在性能方面,虚拟函数调用比非虚函数调用具有更多的开销,更重要的是,您可以假设它通常(总是?)不会内联.添加空基类通常不会将任何代码添加到构造或销毁中,因为空基构造函数和析构函数可以内联到派生类构造函数/析构函数代码中.

如果您想要显式接口,可以使用一些技巧来避免虚函数,但是您不需要动态多态.但是,如果您尝试模拟Java,那么我认为情况并非如此.

示例代码:

#include <iostream>

// A is an interface
struct A {
    virtual ~A() {};
    virtual int a(int) = 0;
};

// B is an interface
struct B {
    virtual ~B() {};
    virtual int b(int) = 0;
};

// C has no interfaces, but does have a virtual member function
struct C {
    ~C() {}
    int c;
    virtual int getc(int) { return c; }
};

// D has one interface
struct D : public A {
    ~D() {}
    int d;
    int a(int) { return d; }
};

// E has two interfaces
struct E : public A, public B{
    ~E() {}
    int e;
    int a(int) { return e; }
    int b(int) { return e; }
};

int main() {
    E e; D d; C c;
    std::cout << "A : " << sizeof(A) << "\n";
    std::cout << "B : " << sizeof(B) << "\n";
    std::cout << "C : " << sizeof(C) << "\n";
    std::cout << "D : " << sizeof(D) << "\n";
    std::cout << "E : " << sizeof(E) << "\n";
}
Run Code Online (Sandbox Code Playgroud)

输出(32位平台上的GCC):

A : 4
B : 4
C : 8
D : 8
E : 12
Run Code Online (Sandbox Code Playgroud)


Chr*_*her 6

C ++中的接口是仅具有纯虚函数的类。例如:

class ISerializable
{
public:
    virtual ~ISerializable() = 0;
    virtual void  serialize( stream& target ) = 0;
};
Run Code Online (Sandbox Code Playgroud)

这不是一个模拟的接口,它是一个类似于Java中的接口,但是没有缺点。

例如,您可以添加方法和成员而不会带来负面影响:

class ISerializable
{
public:
    virtual ~ISerializable() = 0;
    virtual void  serialize( stream& target ) = 0;
protected:
    void  serialize_atomic( int i, stream& t );
    bool  serialized;
};
Run Code Online (Sandbox Code Playgroud)

对于命名约定...在C ++语言中没有定义真正的命名约定。因此,请在您的环境中选择一个。

开销是1个静态表,在尚不具有虚函数的派生类中,它是指向静态表的指针。

  • 我不认为你可以有虚拟构造函数。不过,您可以拥有虚拟析构函数。 (2认同)
  • 我认为析构函数没有理由是纯虚拟的,只是简单的虚拟就足够了。同样声明 dtor 是不够的,它必须被定义。在纯虚 dtor 的情况下,它必须在类定义之外定义,如下所示: ISerializable::~ISerializable() { } 因为 C++ 语法不允许纯虚说明符和类内成员函数定义。 (2认同)