绝不在构造函数和析构函数中调用virtual函数——条款09

关于使用析构函数的几点注意事项 析构函数为你提供了一种机制,可以让你在系统释放对象前做一些善后工作,如清理内存,释放空间等。但是在以下两个方面使用虚构函数时要尤为注意。         1.  多态性 阅读详情

       本条款开始前我要先阐述重点:你不该在构造函数和析构函数期间调用virtual函数,因为这样的调用不会带来你预想的结果,就算有你也不会高兴。

实例:

#include <iostream>
using namespace std;
class AppLogController {
    public:
    AppLogController();
    virtual void logOutput() const;
};

AppLogController::AppLogController() {
    cout<<"AppLogController construction"<<endl;
    logOutput();
}

void AppLogController::logOutput() const{
    cout<<"AppLogController logOutput"<<endl;
}

class AppDebugLogController : public AppLogController {
    public:
    AppDebugLogController();
    virtual void logOutput() const;
};

AppDebugLogController::AppDebugLogController() {
    cout<<"AppDebugLogController construction"<<endl;
}

void AppDebugLogController::logOutput() const{
    cout<<"AppDebugLogController logOutput"<<endl;
}

int main() {
    AppDebugLogController applog;
    return 0;
}

输出:
AppLogController construction
AppLogController logOutput
AppDebugLogController construction

        上述结果可以看出子类构造函数被调用时会先调用基类构造函数,但是虚函数调用的是基类函数不是派生类。

        具体原因是:由于base class构造函数的执行更早于derived class 构造函数,当base class构造函数执行时derived class的成员变量尚未初始化。如果此期间调用virtual函数下降至derived阶层,要知道derived class的函数几乎必然取用local成员变量,而那些成员变量尚未初始化。“要求使用对象内部尚未初始化的成分”是危险的代名词,所以C++不让你走这条路。

        其实还有比上述理由更根本的原因:在derived class对象的base class 构造期间,对象的类型是base class而不是derived class。

        相同道理也适用于析构函数。一旦derived class析构函数开始执行,对象内的derived class成员变量便呈现未定义值,所以C++视它们仿佛不存在。进入base class析构函数后对象就成为一个base class对象,而C++的任何部分包括virtual函数、dynamic_casts等等也就那么看待它。

        如果要让基类能输出派生类信息,则需要将函数改成non-virtual,然后将对应数据传递给基类:

#include <iostream>
using namespace std;
class AppLogController {
    public:
    AppLogController(const std::string& logInfo);
    void logOutput(const std::string& logInfo) const;
};

AppLogController::AppLogController(const std::string& logInfo) {
    cout<<"AppLogController construction"<<endl;
    logOutput(logInfo);
}

void AppLogController::logOutput(const std::string& logInfo) const{
    cout<<"AppLogController logOutput = "<<logInfo<<endl;
}

class AppDebugLogController : public AppLogController {
    public:
    AppDebugLogController():AppLogController("test") {  //给基类传值
        cout<<"AppDebugLogController construction"<<endl;
    }
};

int main() {
    AppDebugLogController applog;
    return 0;
}

输出:
AppLogController construction
AppLogController logOutput = test
AppDebugLogController construction

请记住

  • 在构造和析构期间不要调用virtual函数,因为这个类调用从不下降至derived class(比起当前执行的构造函数和析构函数的那层)。

条款09:绝不在构造和析构过程中调用virtual函数 摘要:在C++中,构造和析构期间调用虚函数存在风险。由于基类构造时派生类成员未初始化,虚函数会被解析为基类版本而非预期的派生类版本(如示例中Transaction基类调用自身logTransaction而非BuyTransaction的实现)。同理,析构时派生类成员已销毁,虚函数也会退化为基类行为。解决方案是通过派生类构造函数将信息向上传递给基类非虚函数处理(如传递日志字符串)。核心原则:构造/析构期间避免虚函数调用,此时对象类型被视为当前构造层级的类,多态机制失效。该规则同样影响RTTI(如dynamic 阅读详情

相关推荐

条款09 绝不在构造和析构过程中调用virtual函数

总结: 在构造或析构期间不要调用 virtual函数,因为这样的调用从不下降至派生类(比起当前执行构造函数和析构函数的那层)。        不应该在构造或析构期间调用 virtual函数,因为这样的调用不会如你想象那样工作,而且会让你很郁闷。作为 Java 或 C# 程序员,也要更加注意本条款,因为这是C++与它们不相同的一个地方。          假设你有一套模拟股票交易的类继

Sunshine的专栏 2260

为什么构造函数不能使用virtual关键字修饰?(学习笔记)

构造函数是用来创建一个新的对象,而虚函数的运行是建立在对象的基础上,在构造函数执行时,对象尚未形成,所以不能将构造函数定义为虚函数,通常析构函数才会用virtual修饰(虚函数实际存放在对象的头部的虚函数表中的) ...

netguang的专栏 3049

构造函数不能声明为虚函数,析构函数可以声明为虚函数

构造函数不能声明为虚函数,析构函数可以声明为虚函数。

构造函数中为什么不能调用虚函数?

构造函数中为什么不能调用虚函数? 构造函数调用层次会导致一个有趣的两难选择。试想:如果我们在构造函数中并且调用了虚函数,那么会发生什么现象呢?在普通的成员函数中,我们可以想象所发生的情况——虚函数的调用是在运行时决定的。这是因为编译时这个对象并不能知道它是属于这个成员函数所在的那个类,还是属于由它派生出来的某个类。于是,我们也许会认为在构造函数中也会发生同样的事情。 然而,情况并非如此。对于在...

线上幽灵 1万+

CentOS 7/8 系统下,基于Docker Compose一键部署Dify AI应用平台

本文详细介绍了在CentOS 7/8系统下,使用Docker Compose一键部署Dify AI应用平台的完整流程。从系统环境准备、Docker引擎安装与加速配置,到获取Dify项目、关键环境变量设置,再到服务启动、状态监控及初始化访问,提供了手把手的部署指南和常见问题排查方法,帮助用户快速搭建稳定、可维护的AI应用开发环境。

q5r6s7的博客 138

初识数据结构之入门必懂——线性表(顺序表)

本文将介绍数据结构中的线性表与线性表中最简单的结构——顺序表

m0_64705485的博客 904

为什么析构函数可以为virtual型,而构造函数则不能?

构造函数不能声明为虚函数,析构函数可以声明为虚函数,而且有时是必须声明为虚函数。不建议在构造函数和析构函数里面调用虚函数。构造函数不能声明为虚函数的原因是:解释一:所谓虚函数就是多态情况下只执行一个。而从继承的概念来讲,总是要先构 造父类对象,然后才能是子类对象。如果构造函数设为虚函数,那么当你在构造父类 的构造函数时就不得不显式的调用构造。还有一个原因就是为了防错,试想如果你在 子类中一不小心重写

爱橙子的OK绷的专栏 2756

某商场G区火灾自动报警与控制系统设计

某商场G区火灾自动报警与控制系统设计 摘要:随着我国经济建设的迅速发展,人民生活水平不断提高,城市用地日益紧张,促使建筑物正朝着高层化、密集化方向发展。高层建筑的特点决定其火灾的危险性和高层建筑的火灾自动报警系统的重要性,一套完整的火灾自动报警及联动控制系统是高层建筑发生火灾时人们生命财产的有利保障,是能否快速准确地发现火情,把火灾扑灭在萌芽状态的关键所在。文章通过消防设计,论述了火灾自动报警及消...

weixin_44026784的博客 5365

Effective C++ 学习笔记 条款09 绝不在构造和析构过程中调用virtual函数

那是每一个C++次成分(见条款1)的态度,而这样的对待是合理的:这个对象内的“BuyTransaction专属成分”尚未被初始化,所以面对它们,最安全的做法是视它们不存在。取而代之的是,对象的行为就像base类型的一样。然而如果logTransaction是个正常的(也就是impure)virtual函数并在Transaction内带有一份实现代码,该版本就会被调用,而程序也会继续向前行,留下你百思不解为什么建立一个derived class对象时会调用错误版本的logTransaction。

tus00000的博客 976

AMBA-CHI协议详解(二)

读事务,写事务,原子事务,Stash事务,Dataless事务,DVM事务,Retry事务。

Crazzy_M的博客 6650

RK3588开发笔记-ISP模块参数调试

本文详细介绍了基于RK3588平台的ISP模块参数调试流程。主要内容包括:硬件准备(RK3588板卡、IMX415摄像头等)、软件环境搭建(Ubuntu系统、RKISP_Tuner工具)、IMX415摄像头基础使用验证、RKISP2.x Tuner工具的使用方法(新建/加载IQ参数文件、Sensor配置、Raw图采集)以及最终IQ文件的部署。文章重点强调了基础标定的重要性,建议开发者分场景调试并参考官方文档,通过合理配置ISP参数来优化图像质量。该调试流程适用于RK3588平台的图像采集类项目开发。

flypig has a dream! 834

Effective C++之条款09:绝不在构造和析构过程中调用virtual函数

声明: 文中内容收集整理自《Effective C++(中文版)第三版》,版权归原书所有。 本内容在作者现有能力的基础上有所删减,另加入部分作者自己的理解,有纰漏之处敬请指正。 条款09:绝不在构造和析构过程中调用virtual函数 Never call virtual functions during construction or destruction. class Transac...

HakunaMatata的博客 260

基于加速科技ST2500的SN74HC00N芯片测试计划

实际上随着工艺的进步,器件内部和引脚间的绝缘氧化膜越来越薄,导致漏电流发生的概率更大,另外制造过程中的工艺缺陷导致的桥接,异物或封装过程中造成的芯片划伤、引裂,都会造成IC的漏电流偏大,有部分产品可能会表现出漏电流偏大,但功能正常,此类产品具有潜在的可靠性问题,漏电流测试的目的就是把具有此类缺陷的产品筛选出来,避免其流到最终产品造成更大的损失。漏电流的测试方法相对简单,就是根据产品手册或测试规范对被测引脚施加额定的电压,然后测量其输入或流出的电流是否符合相应的设计规范。74HC00引脚功能如表所示。

weixin_68288412的博客 1431

条款09:绝不在构造和析构过程中调用virtual函数

Never call virtual function during construction or destruction. 本条款开始前我要先阐述重点:你不该在构造函数和析构函数期间调用virtual函数,因为这样的调用不会带来你预想的结果,就算有你也不会高兴。如果你同时也是一位java或C#程序员,情更加注意本条款,因为这时C++与它们不相同的地方。 假设你有个class继承体系,用来塑模股...

qu1993的博客 278

图文:银行核心账务处理逻辑(白话篇)

银行账务系统分为对内账(记录银行自身资产负债)和对外账(处理客户交易),通过特定编码规则的账号体系进行管理。采用T型账户结构和凭证制度确保每笔交易可追溯,通过明细核算与综合核算双线并行、每日轧账核对(科目日结单、总账、日计表)实现账务精准平衡。从业务触发到最终账平的全流程体现了银行账务"分层记录、双向核对、闭环校验"的核心原则,保障资金流动的透明性与安全性。

银行信息系统应用架构详解 537

通俗易懂的Harris 角点检测

点击上方“3D视觉工坊”,选择“星标”干货第一时间送达Harris 角点检测 认为特征点具有局部差异性。如何描述“特征点具有局部差异性”:以每一个点为中心,取一个窗口,窗口大小为5*5...

3D视觉工坊 1472

Effective C++条款09:绝不在构造和析构过程中调用virtual函数

Effective C++条款09:绝不在构造和析构过程中调用virtual函数(Never call virtual functions during construction or destruction)

CltCj的博客 698

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

[Effective C++]条款9 绝不在构造和析构过程中调用virtual函数

在base class构造期间, virtual函数不是virtual函数, 更细致的说就是base class构造期间virtual函数绝不会下降到derived class阶层. 原因很直观, base class 构造函数执行期间derived class的成员变量一定还未初始化, 如果此刻就可以通过virtual下降到派生类, 我们怎么确保这个虚函数不会调用派生类的成员变量呢? 太危险了, 所以C++不会让你走这条路.

tianmu_sama的博客 366

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

【Effective_c++】条款09:绝不在构造和析构函数中调用virtual函数

条款09:绝不在构造和析构函数中调用virtual函数

夏至亦韵 456

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【复读EffectiveC++09】条款09:绝不在构造和析构过程中调用virtual函数

在构造和析构期间不要调用 virtual 函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层)

weixin_51122602的博客 501

Effective C++条款09:构造/析构/赋值运算之(绝不在构造和析构过程中调用virtual函数)

待续

董哥的黑板报 712

Effective C++ 条款 09:绝不在构造和析构过程中调用 virtual 函数

在构造和析构期间调用virtual函数是危险且不可预测的。明确区分构造/析构期间和对象完全构造后的行为。借助显式函数调用或 non-virtual 函数解决该问题。

weixin_42255569的博客 374

条款 09:绝不在构造和析构过程中调用 virtual 函数

条款 09:绝不在构造和析构过程中调用 virtual 函数 Never call virtual functions during construction or destruction. 假设子类各有不同的记录日志实现,有可能会出现下面这种代码 class T { public: T(); virtual void Log() const = 0; }; T::T() {...

?.?的专栏 364
上一篇: 别让异常逃离析构函数——条款08
下一篇: 令operator=返回一个reference to *this——条款10
OODeveloper
博客等级 码龄12年 43粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值