C++ 在C+中设计ABC(抽象基类)的良好实践+;

C++ 在C+中设计ABC(抽象基类)的良好实践+;,c++,design-patterns,abstract-class,C++,Design Patterns,Abstract Class,在java中,我们可以定义不同的接口,然后我们可以为一个具体的类实现多个接口 // Simulate Java Interface in C++ /* interface IOne { void MethodOne(int i); .... more functions } interface ITwo { double MethodTwo(); ... more functions } class ABC implements IOne, ITwo {

在java中,我们可以定义不同的接口,然后我们可以为一个具体的类实现多个接口

// Simulate Java Interface in C++
/*
interface IOne {
    void   MethodOne(int i);
    .... more functions
}

interface ITwo {
    double MethodTwo();
    ... more functions
}

class ABC implements IOne, ITwo {
    // implement MethodOne and MethodTwo
}
*/
<>在C++中,一般来说,我们应该避免使用多重继承,尽管多继承在某些情况下确实有优势。
class ABC {
public:
    virtual void   MethodOne(int /*i*/) = 0 {}
    virtual double MethodTwo() = 0 {}

    virtual ~ABC() = 0 {}

protected:
    ABC() {} // ONLY ABC or subclass can access it
};
问题1>基于
ABC
的设计,我是否应该改进其他方面,使之成为一个像样的ABC

问题2>一个好的
ABC
是否不应该包含成员变量,而应该将变量保存在子类中

问题3>正如我在评论中指出的,如果
ABC
必须包含太多的纯函数,该怎么办?有更好的办法吗

在C++中,一般来说,我们应该避免使用多重继承< /P> 与任何其他语言功能一样,您应该在适当的地方使用多重继承。接口通常被认为是多重继承的适当使用(例如,请参见COM)

ABC
的构造函数不需要保护——它不能直接构造,因为它是抽象的

ABC析构函数不应该声明为纯虚拟的(当然应该声明为虚拟的)。如果派生类不需要构造函数,则不应要求它们实现用户声明的构造函数

接口不应该有任何状态,因此也不应该有任何成员变量,因为接口只定义如何使用某些东西,而不是如何实现它

ABC
不应该有太多的成员函数;它应该具有所需的确切数字。如果有太多的接口,显然应该删除那些未使用或不需要的接口,或者将接口重构为几个更具体的接口

  • 除非必要,否则不要为纯虚拟方法提供实现
  • 不要使析构函数纯虚拟
  • 不要让构造函数受到保护。不能创建抽象类的实例
  • 最好将构造函数和析构函数的实现隐藏在源文件中,以免污染其他目标文件
  • 使你的界面不可复制
  • 如果这是一个接口,最好不要有任何变量。否则它将是一个抽象基类,而不是一个接口

    过多的纯函数是可以的,除非你可以用更少的纯函数

    基于ABC的设计,我是否应该改进其他方面,使之成为一个像样的ABC

    您有几个语法错误。出于某种原因,不允许将纯虚函数的定义放在类定义中;无论如何,你几乎肯定不想在ABC中定义它们。因此,声明通常是:

    virtual void MethodOne(int /*i*/) = 0;   // ";" not "{}" - just a declaration
    
    尽管析构函数应该是虚拟的(或者在某些情况下是非虚拟的并且受保护的,但是将其虚拟化是最安全的),但是将析构函数变成纯函数并没有任何意义

    不需要受保护的构造函数——它是抽象的,这一事实已经阻止了实例化,除非作为基类

    好的ABC不应该包含成员变量,而应该将变量保存在子类中,这是真的吗

    通常是的。这在接口和实现之间提供了一个清晰的分离

    正如我在评论中指出的,如果ABC必须包含太多的纯函数怎么办?有更好的办法吗


    接口应尽可能复杂,仅此而已。只有“太多”的功能是不必要的;在这种情况下,摆脱他们。如果界面看起来太复杂,它可能试图做不止一件事;在这种情况下,你应该能够把它分解成更小的接口,每个接口都有一个目的。

    第一:为什么我们要避免C++中的多重继承?我从来没见过 一个没有广泛使用它的大型应用程序。继承自 多个接口是使用它的一个很好的例子

    请注意,Java的
    接口
    在您想要使用时就被破坏了 通过契约编程,您必须使用抽象类,并且 他们不允许多重继承。但是,C++中很容易:

    class One : boost::noncopyable
    {
        virtual void doFunctionOne( int i ) = 0;
    public:
        virtual ~One() {}
        void functionOne( int i )
        {
            //  assert pre-conditions...
            doFunctionOne( i );
            //  assert post-conditions...
        }
    };
    
    class Two : boost::noncopyable
    {
        virtual double doFunctionTwo() = 0;
    public:
        virtual ~Two() {}
        double functionTwo()
        {
            //  assert pre-conditions...
            double results = doFunctionTwo();
            //  assert post-conditions...
            return results;
        }
    };
    
    class ImplementsOneAndTwo : public One, public Two
    {
        virtual void doFunctionOne( int i );
        virtual double doFunctionTwo();
    public:
    };
    
    或者,您可以有一个复合接口:

    class OneAndTwo : public One, public Two
    {
    };
    
    class ImplementsOneAndTwo : public OneAndTwo
    {
        virtual void doFunctionOne( int i );
        virtual double doFunctionTwo();
    public:
    };
    
    并从中继承,这是最有意义的

    这或多或少是一个标准的成语;在无法解决的情况下 可以想象接口中的任何前置或后置条件(通常 调用反转),虚拟函数可能是公共的,但一般来说, 它们将是私有的,因此您可以强制执行预处理和预处理 后条件

    最后,注意在很多情况下(特别是如果 表示一个值),您只需直接实现它,而无需 接口。与Java不同,您不需要单独的接口来维护 该实现位于与类不同的文件中 这是C++默认工作方式(类)
    头中的定义,但源文件中的实现代码)。

    不确定实际代码是否与第一个示例块相似,但接口类中的方法声明需要
    virtual
    关键字;只有在最初声明为
    virtual
    后,它才是可选的。是否编译?纯虚函数中0后面的花括号是一个错误MSDN对抽象类说:@AlessandroPezzato:这是正确的;不能在类的主体中定义纯虚拟成员函数。它可以在类的定义之外定义。然而,一些编译器(例如VisualC++,至少Visual C++ 2010 SP1)将接受代码。AlessandroPezzato,为什么MSDN不提供虚拟析构函数?RE 3:构造函数虚拟化没有错,但是…如果类是抽象的,并且没有
    class OneAndTwo : public One, public Two
    {
    };
    
    class ImplementsOneAndTwo : public OneAndTwo
    {
        virtual void doFunctionOne( int i );
        virtual double doFunctionTwo();
    public:
    };