【问题标题】:Protected constructor to make base class not instantiable使基类不可实例化的受保护构造函数
【发布时间】:2013-10-06 02:04:41
【问题描述】:

如果我想避免基类构造函数的实例,保护基类构造函数是一种好习惯吗?我知道我也可以有一个纯虚拟虚拟方法,但这似乎很奇怪......

请考虑以下代码:

#include <iostream>

using std::cout;
using std::endl;

class A 
{
protected:
    A(){};
public:
    virtual void foo(){cout << "A\n";};
};

class B : public A
{
public:
    void foo(){cout << "B\n";}
};

int main()
{
    B b;
    b.foo();
    A *pa = new B;
    pa->foo();

    // this does (and should) not compile:
    //A a;
    //a.foo();
    return 0;
}

是否有我看不到的缺点或副作用?

【问题讨论】:

    标签: c++ inheritance constructor virtual-functions


    【解决方案1】:

    它可以满足您的要求。

    但是,我不确定您从中获得了什么。有人可以写

    struct B : A {
       // Just to workaround limitation imposed by A's author
    };
    

    通常不是在基类中添加无意义的纯虚函数……而是存在纯虚函数,在基类中无法为其提供有意义的实现,这就是它们最终成为纯虚函数的原因。

    无法实例化该类是一个不错的额外属性。

    【讨论】:

    • 这就是意图:我只想实例化派生类。
    • @steffen: 只是因为,我想。
    【解决方案2】:

    通常的做法是保护基类构造函数。如果您的基类中有纯虚函数,则不需要这样做,因为您无法实例化它。

    但是,在基类中定义非纯虚函数并不被认为是好的做法,但在很大程度上取决于您的用例并且没有害处。

    没有任何缺点或副作用。使用受保护的构造函数,您只需告诉其他开发人员您的类仅用作基础。

    【讨论】:

    • 取决于用例,但在具有繁重的类层次结构和复杂算法以及许多可能引入副作用的开发人员难以调试的大型项目中。
    • 例如,当将 C++ 用于安全关键软件时,不应使用覆盖函数(虚拟和非虚拟),因为这会使事情更难验证。
    【解决方案3】:

    使析构函数成为纯虚拟的。每个类都有一个析构函数,而基类通常应该有一个虚拟析构函数,所以你不会添加一个无用的伪函数。

    请注意,纯虚析构函数必须有函数体。

    class AbstractBase
    {
    public:
        virtual ~AbstractBase() = 0;
    };
    inline AbstractBase::~AbstractBase() {}
    

    如果你不希望将析构函数体放在头文件中,请将其放在源文件中并删除 inline 关键字。

    【讨论】:

    • 这是一个有趣的... 坏事是现在我必须做出决定。像往常一样:有不止一种方法可以做到这一点;)
    • 纯虚析构函数只有在类被多态使用时才可行。在没有其他虚函数的类中使用纯虚析构函数会带来不必要的开销。
    • @ArneMertz 是的,但原始示例还包含一个虚函数。
    【解决方案4】:

    您想要实现的通常是通过析构函数而不是构造函数来完成,只是因为您可以使用该函数控制您需要的行为,而不必处理您编写的每个新构造函数。这是一种常见的编码风格/指南,

    • 如果要允许类的实例,请将析构函数设为公开。通常这些类并不意味着继承自。
    • 如果您想在多态上下文中使用和删除该类但又不想允许该类的实例,请将析构函数设为纯虚拟和公共。换句话说,对于多态删除的基类。
    • 如果您不想允许该类的实例并且不想以多态方式删除它的派生类,则将析构函数设为非虚拟且受保护。通常,您根本不想使用它们多态,即它们没有虚函数。

    您选择的后两者中的哪一个是设计决定,无法从您的问题中得到回答。

    【讨论】:

    • 如果您将析构函数标记为纯虚拟 (virtual ~A() = 0;),您还必须在类定义之外提供它的实现:A::~A() { }。当一个对象被销毁时,所有基类的析构函数总是在具体类的析构函数之后被隐式调用,即使在多态删除的情况下也是如此。
    • @bcrist 是的,当然,一个总是必须提供(或让编译器生成)析构函数定义才能使用该类。例外是仅为其静态成员存在的特征和类似类,例如在元编程中。
    • 是的,我想你知道这一点,但我想给看到你的答案但没有意识到的人留个便条。许多经验不足的 C++ 程序员错误地认为纯虚函数不能有实现。
    猜你喜欢
    • 2014-04-22
    • 2014-08-29
    • 1970-01-01
    • 2016-01-26
    • 2015-07-09
    • 1970-01-01
    • 2011-10-23
    • 2017-01-09
    • 2011-05-30
    相关资源
    最近更新 更多