在组织继承结构并需要创建基类抽象时,是否有理由更喜欢纯虚拟析构函数而不是受保护的构造函数(反之亦然)?这个问题已经存在here,但接受的答案并没有真正回答它。
的问题Base *ptr = new Derived();
delete ptr;
可以使用两种方法解决:
根据我的理解,唯一不同的是在第一种情况下尝试Base *ptr = new Base()
:Cannot instantiate abstract class
与第二种情况下Cannot access protected member
的编译错误,首先是更准确。这是风格的问题,还是我不知道的事情?
作为一个有点相关的问题,相同的规则是否会应用于继承链中的某些类,这些类不是严格的基类,但仍然应该是抽象的(即从Monster
继承的GameObject
不能已创建,但Orc
继承自Monster
可以)?
答案 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")
}
}
通过使类抽象化,您可以防止这种可能性。要使类抽象化,只要至少有一个成员函数是纯虚拟的,就不需要将析构函数设置为纯虚拟。如果没有其他成员函数可以合理地生成纯虚函数,那么析构函数就是纯虚拟的。
在回答第二个问题时,基础是在层次结构的顶部还是从其他基础继承并不重要。