深度搜索C++对象模型 - 构造语义学

SKUA-GOCAD入门教程-第二节 软件启动及项目设置 阅读详情


深度搜索C++对象模型:  构造,拷贝,析构语义学

//*********************************************************************************************//
---> "构造函数语义学":
class A
{
    public:
    virtual ~A() = 0;
    virtual int Func() { return m_num; };
    protected:
        int m_num;
};

class B : public A
{

};

观察两个类 我们提出一些问题:
-> 第一 class A 并没有提供构造函数, 当我们需要一个 class B object 时, 如果要对其对象"初始化",
那么责任就落到了 class B 身上, 需要在 class B 里访问 base class 的 protected 对象, 并设定初值,
那么如果当有一天 class B 不在被需要了, 那么 class A 的对象初始化工作也就没有人做了 这并不是一个好的习惯,
所以我们建议自己的 data member, 自己初始化, 任何其他操作会破坏封装;

-> 第二 class A 中有一个纯虚函数, 那么 class A 就不能被实例化, 但是任然可以在子类成员函数中"静态调用"纯虚函数
B::Func2() { A::Func(); };
但是把析构函数设置为了纯虚函数, 那么就一定要定义他, 因为对于派生类来说 他会在"析构时静态调用其基类的析构函数",
如果不定义就会链接失败;

-> 第三 class A 中有一个 virtual int Func(); 但是观察其定义内容, 他完全不和类的类型有关 ( 必须是派生类调用or基类调用 )
他几乎不可能会被后继的派生类改写, 所以此时必须由编译器判断其是否需要改写 vtbl, 但是这么做效率上的报应可谓不轻,

//-----------------------------------------------------------------------------------------------------------------------//
--> "无继承情况下的对象构造":
class A
{

};

A global_a;

A Func()
{
    A local_a;
    A *heap_a = new A;
    *heap_a = local_a;
    delete heap_a;
    return local_a;
}

以上程序表现出三种不同的对象产生方式: "global内存配置", "local内存配置", "heap内存配置";
一个 object 的声明, 是该 object 的一个"执行期属性";
--> "global object" 的生命和整个程序生命相同;
当编译器遇上 global object 的定义时, "观念上的 trivial constructor 和 destructor" ( 实际上并不会产生 观念上的 ), 都会被产生和调用;
constructor 在程序的起始处被调用, destructor 在程序的 exit() 处被调用, ( 事实上 这只是一个观念上的东西, 因为他压根就不会产生构造和析构 );
而在C++中, 全局对象被认为是完全定义的, 他会阻止第二个或者更多定义,

-> "local object" 的生命从定义开始, 到作用域结束 ( 遇上 } );
事实上 local object 同样是没有被构造也没有被析构, 如果程序在之后就要使用他的初值 那么没有构造就是一个败笔;

-> "heap object" 的声明从他被 new 运算符配置出来开始, 直到他被 delete 运算符摧毁为止;
而对 heap object 的初始化操作, 会被转化为对 new 运算符的调用( 由 library 提供 ): A *heap_a = __new( sizeof( A ) );
此时 并没有默认构造函数施加于 __new 传回来的 object 身上,
而对 heap object 的delete操作, 会被转化为对 delete 运算符的调用( 由 library 提供 ): __delete( heap_a );

之后,  *heap_a = local_a;  这只是一个赋值操作, 如果 local_a 里是正确的数据, 那么一切都没有问题;

总结: "观念上 这些操作都会触发我们所谓的 构造函数, 复制构造函数, 析构函数, 但事实上并不是";
我们认为会触发构造函数的地方, 都不会触发, 因为并没有需要构造函数来初始化的 object;
我们认为会触发复制构造函数的地方, 都不会触发, 因为这些操作都是由"位拷贝操作"完成, 完全不需要复制构造函数;
我们认为会触发析构函数的地方, 都不会触发, 理由同构造函数, 完全没有需要析构函数来删除的object;

class A
{
    public:
    A( int num = 0 ) : m_num( num ) { };
    private:
    int m_num;
};

A global_a;

A Func()
{
    A local_a;
    A *heap_a = new A;
    *heap_a = local_a;
    delete heap_a;
    return local_a;
}

分析这个程序和上面程序的区别, 唯一就是类中多了一个 "自己定义的构造函数";
( 题外话: 如果要对一个值初始化, 那么给出一个初始化成员列表 就像这个程序, 会比 相同语义的构造函数效率高 )
现在 在 构造出的三个对象里, 他们"都会调用到我们所定义的构造函数", 剩余的都和上面一样;
注意, 这些操作"仍然不会导致析构函数或者复制构造函数的调用", 因为我们根本没有定义他们;

//-----------------------------------------------------------------------------------------------------------------------//
--> "继承情况下的对象构造":
class A
{
    public:
    A() { std::cout << "Constructor A" << Size() << std::endl; }
    virtual int Size() { return sizeof( A ); };
    int m_num_a;
};

class B : public A
{
    public:
    B() { std::cout << "Constructor B" << Size() << std::endl; }
    virtual int Size() { return sizeof( B ); };
    int m_num_b;
};

class C : public B
{
    public:
    C() { std::cout << "Constructor C" << Size() << std::endl; }
    virtual int Size() { return sizeof( C ); };
    int m_num_c;
};

如果我们 C c;会怎么样 ?
先讨论一下构造函数, 当我们构造 c 的时候; 编译器会在 C::C() 中内插代码 来初始化其 subobject,
class C 扩张: C::C() { B::B(); }    class B 扩张: B::B() { A::A(); }
根据这个扩张我们发现 其实他们是一个递归调用, 而且内插的代码在"执行代码之前";
所以结果应该是先输出最内层的代码, 在逐次往外, 这就解释了为什么 输出结果的一部分是 A B C;

在讨论 内含的 virtual 函数, 我们构造 C c; 的时候 内含的三个 Size() 函数会被怎么决议?
毕竟我们在构造的是 C c; 那么他会被决议为 C::Size() ?  或者是我们正在构建的 this::Size() ?
在C++语言规则中有一条: "在一个 class 中, 经由构造中的对象来调用一个 virtual function, 其函数实例应该是此 class 中有作用的那个";
通俗点说: 在构造过程中调用虚函数, 那么构造到哪一步 调用到哪一步; 所以结论是: 8 12 16 ( vptr + data member );

总结: "通常 constructor 的执行算法":
-> 在 derived class constructor中, 所有 virtual base class 以及上一层 base class 的 constructor 会被调用;
( 解释一下: 如果是虚拟继承 会直接由构建的 derived class object 来构造其虚拟继承的 base class, 而且只构造一次 通过一个标志,条件确定是否构造过,
然后调用一次 上一层的 base class constructor )
-> 上述完成之后, 对象的 vptr 被初始化, 指向合适的 vtbl;
-> 如果有 member init list的话 将在 constructor 中扩展开, 这必须在 vptr 初始化之后;
-> 最后 执行程序员提供的代码;

所以一个构造函数一般被构建为如下形态: ( A为 base class, B虚拟继承A, C继承B )
C::C()
{
    if( __most__derived != false )         // 条件判断虚基类, 初始化之后把标志位设置为false;
        this->A::A();
   
    this->B::B();                               // 无条件调用上一层 base class 的构造函数
   
    this->vptr = &vtbl;                       // vptr 的初始化
   
    //code                 
}


//*********************************************************************************************//
---> "对象复制语义学( operator=() )":
当我们设计一个 class, 并以一个 class object 指定给另一个 class object 时, 我们有三种选择:
-> 什么都不作 实施默认行为;
-> 提供一个i额显示的 operaotr=();
-> 显示的拒绝这个行为;

如果要实现第三点, 把 operator= 声明为 private 就可以了;
现在的问题是:我们什么时候需要一个 显示的 operator=() ?
如果我们要支持的只是一个简单的拷贝操作, 而且默认行为已经足够 我们没有理由去提供他, 只有当默认行为导致不安全或不正确时 我们才应该去设计他;
事实上 只有我们需要拷贝的是一个 subobject 或者 需要拷贝一个 pointer 指向的堆我们需要 复制构造函数和对象复制 其他时候 我们并不需要;
那么什么时候用复制构造函数, 什么时候用对象复制?
A a;
A b = a;    // 对于这种情况 表面上一个对象复制 实际上因为此时要新建一个对象b, 所以调用的是拷贝构造函数;
a = b;      // 对于已经新建好的对象, 赋值之间调用的就是 对象复制 operator=();


//*********************************************************************************************//
---> "析构函数语义学":
"析构函数被编译器合成的条件和构造函数不同":
--> 在 class 内含 member object, 而且这个 member object 有析构函数;
--> 这个类是上述类的派生类;
"就算有 virtual function 机制, 仍然不一定合成析构函数"

那么什么时候会"有一个析构函数"? ( 不管是合成了还是声明了 )
--> 当我们在构造函数里分配了某些资源的时候 我们自然要在析构函数里收回资源
那么什么时候我们"需要一个虚析构函数"?
--> 当我们希望使用一根父类的指针来对子类对象执行delete操作的时候,
如果不是虚析构函数, 那么他即将调用的是父类的析构函数, 这不合适, 所以需要虚析构函数让他来自动根据对象来调用子类的析构函数;

那么析构函数都干了什么?
--> 首先 destructor 本身首先被执行;
--> 如果 class 拥有 member class object, 而且这个 member class object 拥有 destructor, 那么会以声明顺序反向调用;
--> 如果 object 内含 vptr, 此时调整 vptr, 指向合适的 vtbl;
--> 如果上一层 base class 拥有 destructor, 按照声明顺序反向调用其 destructor;
--> 如果 virtual base class 拥有 destructor, 按照声明顺序反向调用其 destructor;

所以一个析构函数很可能被扩张为如下形式:( A 为 base class, B 继承 A 且内含X, C 继承 B 且内含 X, 他们都有析构函数 和 vptr )
C::~C()
{
    // code;
    X::~X();
    this->vptr = vtbl;
    B::~B();
}
   
B::~B()
{
    // code;
    X::~X();
    this->ptr = vtbl;
    A::~A();
}

//*********************************************************************************************//

OSCP-Algernon(SmarterMail) 从前面的结果中,确实有.NET远程处理在端口17001上运行。因此,此漏洞应适用于目标计算机。下载了漏洞并编辑了以下部分以匹配我的IP并使用本地端口21。端口9998引导到一个SmarterMail的登录页面。设置监听,获得一个反弹shell。端口80重定向到默认IIS页。已经是system权限了。google搜索漏洞。 阅读详情

相关推荐

PLC编程入门基础知识

PLC即为可编程逻辑控制器,实质是一种专用于工业控制的计算机,其硬件结构基本上与微型计算机相同。

SANRENYIXING的博客 2万+

深度搜索C++对象模型2.2 构造函数语义学-Copy Constructor的构造操作

Shallow Copy = Bitwise Copy,Deep Copy = Memberwise Copy.

春秋战车 1066

STM32平台下FreeModbus TCP协议栈集成指南

深入讲解在STM32平台如何移植和配置freemodbus协议栈,实现TCP通信功能。结合嵌入式开发细节,帮助开发者快速掌握freemodbus的应用与调试技巧。

weixin_36001279的博客 1144

深度搜索C++对象模型2.2 构造函数语义学-Default Constructor的构造操作

implicit 暗中的 explicit 显式的 trivial 没用的 no

春秋战车 965

FOC电机控制中电流采样延迟补偿的原理和方法

摘要: FOC电机控制中,电流采样、计算与PWM更新存在固有延迟(约1.5个PWM周期),导致相位滞后,限制电流环带宽。主流补偿方法包括: 一阶前向预测法:基于历史电流值预测未来电流,抵消延迟,工程应用广泛; 相位超前补偿:通过s/z域校正器设计实现等效预测; 状态观测器法(如龙伯格观测器):通过电机模型闭环估计状态,兼具噪声抑制和参数辨识能力,但复杂度高。实际需注意参数敏感性、离散化精度及延迟时间校准,避免过补偿风险。一阶预测法因平衡效果与复杂度成为工业首选。

jifengzhiling的博客 2232

SSCardDriver.dll社会保障卡读写终端接口规范38号文接口

********************获取设备及PSAM卡信息********************//********************获取银行账户信息 ********************//********************获取设备信息********************//********************PIN校验********************//********************通用写卡********************/

广东东信智能科技有限公司的专栏 1745

通过对象指针的方式强行指定到子类_c++对象模型

使用c++有些年头了,有一本深度搜索c++对象模型的书写的很赞,很经典。本文是本书的读书笔记。关于对象加上封装后的布局成本c语言中如下声明一个结构体typedef struct point3d{ float x; float y; float z;}Point3d;struct point3d 转化为class Point3d之后class Point3d{ public: Point3d(flo...

weixin_39517289的博客 94

BLDC方波控制双闭环Simulink仿真模型

无刷直流电机方波控制双闭环Simulink仿真模型搭建详细教程,包括速度PI环、电流PI环、换向逻辑和受控电压源,仿真结果良好。

m0_57458432的博客 6870

深度搜索C++对象模型(理念总结)

1. 目标: C++的语法规格(C++ 标准)如何具体实现出来的, 即编译器如何理解并构建一系列的数据结构, 去扩充/优化/翻译cpp源代码,然后组成串行指令,确保最终代码能够准确而高效执行. 大致包括1). 编译器功能的思考和设计逻辑                         2).cpp源代码的编译+链接(不用生成汇编)的步骤. 2.构造函数语义学: 遵循语意(C++ 标准

noticmp的专栏 769

告别手绘!用Aseprite+Unity Tilemap,从AI生成到2D游戏地图的保姆级搭建流程

本文详细介绍了如何利用Aseprite和Unity Tilemap从AI生成素材到搭建2D游戏地图的完整流程。涵盖AI生成素材的预处理、图层管理、动画制作,以及Unity中的Tilemap系统配置和性能优化技巧,帮助开发者高效创建像素风游戏地图。

weixin_29015483的博客 583

ShaderJoy —— 简单又神奇的 “合体” 函数【GLSL】

smin 函数 它实际上有两种常见的实现方式 多项式 指数 多项式 smin 函数图 其对应的代码为 float smin( float a, float b ) { // 多项式 const float k = 0.5; float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0); return mix(b, a, h) - k * h * (1.0 - h); } 从上图中我们可以很明显的看出有两处不连...

ShaderJoy 的兴趣技术杂货铺 1857

对象传参_c++对象模型

使用c++有些年头了,有一本深度搜索c++对象模型的书写的很赞,很经典。本文是本书的读书笔记。关于对象加上封装后的布局成本c语言中如下声明一个结构体typedef struct point3d{ float x; float y; float z;}Point3d;struct point3d 转化为class Point3d之后class Point3d{ public: Point3d(flo...

weixin_32821643的博客 180

瑞芯微RK3588部署YOLOv8模型

写下这篇博客进行记录,以防后面忘了怎么部署。

weixin_45303602的博客 3664

深度搜索C++对象模型 - 数据语义学

 深度搜索C++对象模型: Data Semantics //*********************************************************************************************// ---> "虚拟继承的 sizeof 问题": class X { }; class A : virtual public X {

github_21089181的专栏 454

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

深挖C++:类和对象(中)

(先进后出)

xys_678的博客 242

Camera2Demo

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

C++移动构造函数详解

移动构造函数是C++11引入的一种特殊构造函数,用于从临时对象(右值)"窃取"资源来初始化新对象,而不是进行昂贵的深拷贝。char* data;public:// 移动构造函数:参数是右值引用 &&// 关键:把源对象的资源置空。

拒绝AI文,努力在AI时代写出感动人心的好文章 391

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

ThunderKittens 深度解析:一个不基于 MLIR 的 C++ 模板 GPU DSL

Triton 是"让更多人能写 GPU kernel",ThunderKittens 是"让会写的人写得更爽"。前者用 MLIR 换易用性,后者用 C++ 模板换可控性。它们不是替代关系,而是覆盖了 GPU 编程光谱的两端。ThunderKittens 的出现,本质上是对"GPU DSL 必须基于 MLIR"这一默认假设的挑战。编译期类型系统本身就可以是一个强大的 DSL 基础设施,不需要中间 IR,不需要黑盒 pass,也能实现 tile 级抽象和零运行时开销。

Maxwell_Newton的博客 339

跟我学c++中级篇—类型定义的方式

本文系统阐述了编程中类型定义的重要性及c++中的实现方式。重点对比了宏定义、typedef与modern using三种机制,指出using在可读性、灵活性和模板支持上的优势,推荐优先使用。同时提醒开发者注意别名滥用、类型歧义、错误信息冗长等问题,强调统一规范与合理设计。最终结论:在现代c++开发中,应以using为主,兼顾简洁性与可维护性,避免陷入命名混乱与编译困境。

fpcc的专栏 71

【C语言】字符与字符串函数

C语言中字符串为以\0结尾的字符数组,标准库函数基于此约定。本文解析字符串内存布局,详解ctype.h与string.h中字符分类、长度计算、拷贝、比较等函数的使用要点,强调长度判断与越界风险,最后通过命令行待办事项示例整合fgets、sscanf、strncpy、strcmp,展示实际应用中的关键细节。

记录在学习过程中学到的知识 347

C++ 函数模版

模板参数:● template ,T 就是类型占位符,名字可以自定义,习惯用T● 占位符可以在函数返回值、形参、函数内部当作类型使用使用要点模板代码一般写在头文件:编译器需要看到完整模板源码,才能在每个编译单元完成实例化全局函数、类内成员函数都可以写成模板实例化发生在编译阶段,不是运行时隐式实例化:调用时编译器自动推导实参类型,生成对应版本函数。

2501_94041724的博客 304

C++ 运行时多态与虚函数机制全面剖析【面试重点】

构成C++ 运行时多态必须是基类的指针或者引用调用虚函数。被调用的函数必须是虚函数,并且派生类必须对该虚函数完成重写/覆盖(Override)。只有基类的指针或引用才能既指向基类对象,又指向派生类对象(切片机制);而只有完成了虚函数重写,派生类与基类才具备不同的函数行为,达到“指向谁就调用谁”的效果。在类成员函数的前面加上virtual关键字修饰,该成员函数就成为了虚函数。public:// 成员函数前加 virtual 修饰,声明为虚函数。

Fuyo_0209的博客 437

#SQLite 数据库笔记一:(概念 / 终端指令 / SQL 语句 / C 编程 / 回调函数)

数据库 = 数据的仓库,假设数据是海量。能力说明存储数据持久化保存,程序退出数据还在统计数据、分组、排序数据管理增、删、改、查(CRUD)嵌入式设备上也会用数据库(单片机网关、路由器、工业 HMI 都常见),因为 sqlite 体积小、无需服务进程、单文件即可。数据库 = 库 > 表 > 记录(行) > 字段(列)sqlite3 = 开源、C 写的、单文件、免安装、最多 2T 的嵌入式关系型数据库。

lwq20051110的博客 213

C 语言 malloc() 详解与实战 Demo

本文详解C语言malloc()函数,涵盖其语法、内存原理及实战应用。通过动态整数、数组、结构体与链表的Demo,演示如何安全申请、使用与释放内存,强调避免内存泄漏与空指针检查的重要性,帮助开发者掌握动态内存管理核心技能。

tangPHP的博客 372

【C++ 基础入门】类和对象(上):封装、对象模型与 this 指针

C++类与对象知识(面向过程和面向对象的初步认识,类的初步认识,类的访问限定符及封装,类的作用域、实例化,类的对象模型,this指针)

2601_96587588的博客 885

【C++ 基础入门】C++继承(下):问题、底层原理与组合

C++继承(菱形继承的问题、虚继承原理、继承与组合、构造顺序)

2601_96587588的博客 791

蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级 备考:莫队算法离线区间查询详解

备赛进行时:第十八届蓝桥杯软件赛报名已于 2026-10-08 10:00 开启、12-18 14:00 截止,省赛定在 2027-04-10(C/C++·Python 程序设计 9:00–13:00);中国电子学会 2026 年 12 月软件编程(C/C++ 一级至十级、Python 一级至六级)考级报名 9/23–12/2 16:00、统考 12-12;全国青少年信息素养大赛 2026—2027 赛季算法应用主题赛也已进入复盘练兵窗口。

IT_Scratch的博客 139
上一篇: 深度搜索C++对象模型 - 数据语义学
下一篇: C++11可变参数函数与for循环
zhang_yao_wu1993
博客等级 码龄12年 0粉丝 18原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值