【C++】析构函数和virtual函数引发的隐晦问题 ——《Effective C++》

用工厂模式的时候,我们常常将一个基类指针指向子类对象,以此来实现多态。但是,当调用delete析构这个基类指针的话,就有一个潜藏的危机: 当子类对象经由一个基类指针被删除,而该基类带着一个非virtual析构函数,其结果是不能预料的——实际执行时通常发生的是对象的子类成分没有被销毁!
消除这个问题的做法很简单: 给基类一个virtual析构函数

如果一个类不含有virtual函数,通常表示它并不是准备被作为基类。不要为这样一个类也加上virtual函数!否则一来增加运行开销,二则因为vbr的加入使得对象体积变大,还会使得对象内部的细节混乱,无法和其他语言的函数通信。
经验是这样: 1 只有当类中含有virtual函数时,我们才为它声明virtual析构函数。 2 不带virtua;析构函数的指针的类最好别做基类。 3 不要试图继承一个标准容器或任何其他带有非virtual析构函数的类。

当你想拥有一个抽象类,但是没有纯虚函数时,怎么办?
答案是 为这个类声明一个纯虚的析构函数,然后再为这个析构函数提供一份定义(因为析构子类时必须要调用它)。这个规则只是针对于那些设计目的为了多态的类。

忠告:

带有多态性质的基类应该声明一个virtual析构函数,如果一个类有任何的virtual函数,它就应该拥有一个virtual
析构函数。

如果类的设计目的不是为了作为基类使用,或者不是为了具备多态性,就不应该声明virtual析构函数。



在类的析构函数抛出异常时,程序可能过早结束或者出现不明确行为(资源泄露)。C++不喜欢析构函数抛出异常!
常规的办法: 1 如果捕捉到析构函数异常,就强制推出程序,避免不明确行为传播。 2 忽略异常(捕捉到异常不做任何事)。
较好的方法: 写一个普通函数来做析构函数的事情,然后在析构函数里调用这个函数。允许用户调用Close(即使用户不显式调用,也会在析构函数里隐式调用),这样就给了用户一个处理可能出现的异常的机会。

忠告:

析构函数绝对不要抛出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后忽略或者强制程序结束。

如果客户需要对某个操作函数运行期间排除的异常做出反应,那么应该提供一个普通函数(而不是在析构函数中)执行该操作。


如果你想要在构造函数中调用virtual函数,你不会得到你想要的结果。基类构造期间virtual函数绝不会下降到子类。对象的作为就像是基类一样。实际上,在基类构造期间,virtual函数不是virtual函数。因为在基类构造的时候,子类成分还没有构造,怎么可能有函数供调用!
同样的道理适合于析构函数。假如在基类的析构函数中调用了virtual函数,执行的时候其实子类成分已经被析构掉了。

唯一能够避免此问题的做法就是:确定你的构造函数和析构函数的整个执行过程中都没有调用virtual函数。

如果你想要实现一些成员的多态化构造,应该又子类像父类的某些函数传递一些信息,然后由这些函数来进行多态构造,而不是弄一些virtual函数在构造函数里。“由于你无法使用virtual函数从基类向下调用,在构造期间,你可以令子类将必要的构造信息向上传递到基类构造函数中。”

忠告:

在构造和析构期间不要调用virtual函数,因为这类调用从不下降到子类中。

用工厂模式的时候,我们常常将一个基类指针指向子类对象,以此来实现多态。但是,当调用delete析构这个基类指针的话,就有一个潜藏的危机: 当子类对象经由一个基类指针被删除,而该基类带着一个非virtual析构函数,其结果是不能预料的——实际执行时通常发生的是对象的子类成分没有被销毁!
消除这个问题的做法很简单: 给基类一个virtual析构函数

如果一个类不含有virtual函数,通常表示它并不是准备被作为基类。不要为这样一个类也加上virtual函数!否则一来增加运行开销,二则因为vbr的加入使得对象体积变大,还会使得对象内部的细节混乱,无法和其他语言的函数通信。
经验是这样: 1 只有当类中含有virtual函数时,我们才为它声明virtual析构函数。 2 不带virtua;析构函数的指针的类最好别做基类。 3 不要试图继承一个标准容器或任何其他带有非virtual析构函数的类。

当你想拥有一个抽象类,但是没有纯虚函数时,怎么办?
答案是 为这个类声明一个纯虚的析构函数,然后再为这个析构函数提供一份定义(因为析构子类时必须要调用它)。这个规则只是针对于那些设计目的为了多态的类。

忠告:

带有多态性质的基类应该声明一个virtual析构函数,如果一个类有任何的virtual函数,它就应该拥有一个virtual
析构函数。

如果类的设计目的不是为了作为基类使用,或者不是为了具备多态性,就不应该声明virtual析构函数。



在类的析构函数抛出异常时,程序可能过早结束或者出现不明确行为(资源泄露)。C++不喜欢析构函数抛出异常!
常规的办法: 1 如果捕捉到析构函数异常,就强制推出程序,避免不明确行为传播。 2 忽略异常(捕捉到异常不做任何事)。
较好的方法: 写一个普通函数来做析构函数的事情,然后在析构函数里调用这个函数。允许用户调用Close(即使用户不显式调用,也会在析构函数里隐式调用),这样就给了用户一个处理可能出现的异常的机会。

忠告:

析构函数绝对不要抛出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后忽略或者强制程序结束。

如果客户需要对某个操作函数运行期间排除的异常做出反应,那么应该提供一个普通函数(而不是在析构函数中)执行该操作。


如果你想要在构造函数中调用virtual函数,你不会得到你想要的结果。基类构造期间virtual函数绝不会下降到子类。对象的作为就像是基类一样。实际上,在基类构造期间,virtual函数不是virtual函数。因为在基类构造的时候,子类成分还没有构造,怎么可能有函数供调用!
同样的道理适合于析构函数。假如在基类的析构函数中调用了virtual函数,执行的时候其实子类成分已经被析构掉了。

唯一能够避免此问题的做法就是:确定你的构造函数和析构函数的整个执行过程中都没有调用virtual函数。

如果你想要实现一些成员的多态化构造,应该又子类像父类的某些函数传递一些信息,然后由这些函数来进行多态构造,而不是弄一些virtual函数在构造函数里。“由于你无法使用virtual函数从基类向下调用,在构造期间,你可以令子类将必要的构造信息向上传递到基类构造函数中。”

忠告:

在构造和析构期间不要调用virtual函数,因为这类调用从不下降到子类中。

用工厂模式的时候,我们常常将一个基类指针指向子类对象,以此来实现多态。但是,当调用delete析构这个基类指针的话,就有一个潜藏的危机: 当子类对象经由一个基类指针被删除,而该基类带着一个非virtual析构函数,其结果是不能预料的——实际执行时通常发生的是对象的子类成分没有被销毁!
消除这个问题的做法很简单: 给基类一个virtual析构函数

如果一个类不含有virtual函数,通常表示它并不是准备被作为基类。不要为这样一个类也加上virtual函数!否则一来增加运行开销,二则因为vbr的加入使得对象体积变大,还会使得对象内部的细节混乱,无法和其他语言的函数通信。
经验是这样: 1 只有当类中含有virtual函数时,我们才为它声明virtual析构函数。 2 不带virtua;析构函数的指针的类最好别做基类。 3 不要试图继承一个标准容器或任何其他带有非virtual析构函数的类。

当你想拥有一个抽象类,但是没有纯虚函数时,怎么办?
答案是 为这个类声明一个纯虚的析构函数,然后再为这个析构函数提供一份定义(因为析构子类时必须要调用它)。这个规则只是针对于那些设计目的为了多态的类。

忠告:

带有多态性质的基类应该声明一个virtual析构函数,如果一个类有任何的virtual函数,它就应该拥有一个virtual
析构函数。

如果类的设计目的不是为了作为基类使用,或者不是为了具备多态性,就不应该声明virtual析构函数。



在类的析构函数抛出异常时,程序可能过早结束或者出现不明确行为(资源泄露)。C++不喜欢析构函数抛出异常!
常规的办法: 1 如果捕捉到析构函数异常,就强制推出程序,避免不明确行为传播。 2 忽略异常(捕捉到异常不做任何事)。
较好的方法: 写一个普通函数来做析构函数的事情,然后在析构函数里调用这个函数。允许用户调用Close(即使用户不显式调用,也会在析构函数里隐式调用),这样就给了用户一个处理可能出现的异常的机会。

忠告:

析构函数绝对不要抛出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后忽略或者强制程序结束。

如果客户需要对某个操作函数运行期间排除的异常做出反应,那么应该提供一个普通函数(而不是在析构函数中)执行该操作。


如果你想要在构造函数中调用virtual函数,你不会得到你想要的结果。基类构造期间virtual函数绝不会下降到子类。对象的作为就像是基类一样。实际上,在基类构造期间,virtual函数不是virtual函数。因为在基类构造的时候,子类成分还没有构造,怎么可能有函数供调用!
同样的道理适合于析构函数。假如在基类的析构函数中调用了virtual函数,执行的时候其实子类成分已经被析构掉了。

唯一能够避免此问题的做法就是:确定你的构造函数和析构函数的整个执行过程中都没有调用virtual函数。

如果你想要实现一些成员的多态化构造,应该又子类像父类的某些函数传递一些信息,然后由这些函数来进行多态构造,而不是弄一些virtual函数在构造函数里。“由于你无法使用virtual函数从基类向下调用,在构造期间,你可以令子类将必要的构造信息向上传递到基类构造函数中。”

忠告:

在构造和析构期间不要调用virtual函数,因为这类调用从不下降到子类中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值