纯虚拟析构函数与受保护构造函数+虚拟dtor

时间:2015-10-23 09:41:34

标签: c++ inheritance abstract-class

在组织继承结构并需要创建基类抽象时,是否有理由更喜欢纯虚拟析构函数而不是受保护的构造函数(反之亦然)?这个问题已经存在here,但接受的答案并没有真正回答它。

的问题
Base *ptr = new Derived();
delete ptr;

可以使用两种方法解决:

  • 使dtor纯虚拟并为其定义主体
  • 使dtor虚拟并使ctor受保护

根据我的理解,唯一不同的是在第一种情况下尝试Base *ptr = new Base()Cannot instantiate abstract class与第二种情况下Cannot access protected member的编译错误,首先是更准确。这是风格的问题,还是我不知道的事情?

作为一个有点相关的问题,相同的规则是否会应用于继承链中的某些类,这些类不是严格的基类,但仍然应该是抽象的(即从Monster继承的GameObject不能已创建,但Orc继承自Monster可以)?

2 个答案:

答案 0 :(得分:5)

如果只有默认构造函数是protected,您仍然可以使用隐式定义的复制构造函数创建实例:

struct ProtectedBase
{
    virtual ~ProtectedBase() = default;
protected:
    ProtectedBase(){}    
};

struct ProtectedDerived : ProtectedBase {};

struct VirtualBase {
    virtual ~VirtualBase() = 0;
};

VirtualBase::~VirtualBase() = default;

struct VirtualDerived : VirtualBase {};

int main() {
    ProtectedBase a { ProtectedDerived{} }; //valid
    VirtualBase b { VirtualDerived{} };     //invalid
}

因此,您需要将所有构造函数显式标记为protected,包括任何隐式定义的构造函数。这似乎比简单地创建纯虚拟析构函数并实现它更多。错误地错过了很多东西也很容易。

在更概念的层面上,如果你想把一个类抽象化,那么把它变成抽象的。只要你不长时间凝视,实现一些让它看起来像一个抽象类的东西对你来说并不是很有帮助。

答案 1 :(得分:1)

如果你有一个带有受保护构造函数的非抽象类,你仍然可以在派生类的方法中不合需要地实例化该类,因为它可以访问基类的受保护成员:

var audioPlayer : AVAudioPlayer!

func playSound(soundName: String)
{
    let coinSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(soundName, ofType: "m4a")!)
    do{
        audioPlayer = try AVAudioPlayer(contentsOfURL:coinSound)
        audioPlayer.prepareToPlay()//there is also an async version
        audioPlayer.play()
    }catch {
        print("Error getting the audio file")
    }
}

通过使类抽象化,您可以防止这种可能性。要使类抽象化,只要至少有一个成员函数是纯虚拟的,就不需要将析构函数设置为纯虚拟。如果没有其他成员函数可以合理地生成纯虚函数,那么析构函数就是纯虚拟的。

在回答第二个问题时,基础是在层次结构的顶部还是从其他基础继承并不重要。