C/C++ 杂记-1

编译

Table 1 预处理行为

序号 步骤名称 工作内容
1 头文件展开 将所有#include引入的头文件递归嵌入到源文件中
2 宏文本替换 代码中所有宏标识符替换为定义的文本内容
3 去除注释 删除代码里全部单行//、多行/* */注释
4 条件编译展开 解析#ifdef/else/endif,剔除不生效的代码分支

Table 1 Preprocessing

Serial Number Step Name Work Details
1 Header file expansion Recursively expand all included header files into the source file
2 Macro text replacement Replace all macro names with corresponding literal text
3 Comment elimination Delete all single-line and multi-line comments in code
4 Conditional compilation expansion Parse #ifdef/#else/#endif and discard unreachable code branches

Table 2 编译链接核心工作

操作项 详细说明
数据段合并 合并所有目标文件的全局变量、静态变量、常量字符串到统一段区
地址回填(重定位) 解析未定义符号,为代码引用填充正确内存地址
库引入 加载静态库/动态库,完成跨文件外部符号解析
Operation Item Specific Description
Data segment merging Merge global variables, static variables, string constants from all object files into unified segments
Address backfilling (relocation) Resolve undefined symbol addresses and fill correct memory addresses for references
Library introduction Load static/dynamic libraries, resolve external cross-file symbols

局部数组0初始化

局部数组 char str20; 存储随机垃圾值,不会自动全部置零

char str20 = {};//置零,C++98

char str20{};//置零,C++11 引入

全局 / 静态数组 char str[20];(定义在函数外部)无需手动赋值,会自动全部置零。

  • English: Local variables are allocated on the stack, and stack memory is not cleared automatically by the compiler.

    局部变量分配在栈内存,栈内存不会由编译器自动清零。

  • English: If you write char str[20] = {0};, only the first element is set to 0, and all remaining elements are zero-initialized implicitly.

    如果写 char str[20] = {0};,仅第一个元素显式设为 0,剩余所有元素会被隐式初始化为 0。


静态成员变量

类内声明,类外定义

为什么不用构造函数初始化化静态成员变量?

因为静态成员变量可以直接用类名访问,不需要依赖对象,这时候不经过构造函数,没有采用构造函数内的初始化条件


sizeof(空类)=1

每个对象都应该在内存上有独一无二的地址,


不要重载&&和||

逻辑与和逻辑或, 无法模拟其短路特性

operator&&和operator||的操作数是对象而不是表达式,需要先得到/计算出具体的对象才能进行,因此无法短路


singleton中从指针拿到单例对象的引用

singleton模式的类class Printer 的唯一 static Printer * printer;

用引用方式取得 Printer & ref_printer = * printer; 这就从指针拿到了对象

可以使用对象来调用重载函数,例如打印函数的成员函数operator() 可以直接使用


operator= 自赋值判断

== this==&person==

若为自赋值,则可能会导致误删自己的内存,这是无法恢复的

要记得return *this


值初始化 (value‑initialization) new int10 ();

基础数据类型T

new (T)10 (); +()可以初始化为0,这是C++98的标准写

(与类的默认构造一致,基础数据类型也可以这样)

cpp 复制代码
new int[10];     // 默认初始化
new int[10]();   // 值初始化
new int[10]{ };  // C++11以后,列表初始化
  • 对自定义类类型
cpp 复制代码
struct A { int x; };

A* arr = new A[10](); 中 ()触发值初始化:会调用A()默认构造函数逐个构造对象。

不带括号:A* arr = new A[10];同样调用默认构造;类类型默认初始化与值初始化行为大多一致;差异主要体现在内置基础类型


模拟实现vector不要全部初始化

vector使用push_back()插入对象,插入一个初始化一个即可,没有必要在分配内存时对所有元素进行零初始化,这样做会带来大量无用开销。

std::vectorreserve() 只会分配原始的、未初始化的内存块(raw memory)不会构造任何对象,不会做零初始化 ;等到push_back/emplace_back的时候,再用定位 new在空闲内存上逐个构造对象。

  • 如果 reserve 分配内存的时候,就把 capacity 全部的空间全部初始化 / 置零,当 capacity 很大、实际 size 很小的时候,会产生大量完全没有必要的构造、清零开销。
  • vector 底层需要拿到原始、未初始化的内存,只负责内存的申请,对象构造推迟到 push_back。

该结论针对reserve;如果是vector(n)resize(n),业务语义要求存在 n 个有效对象,则必须完成对应对象构造。


重载\[\]的operator\[\]一定要进行下标index的判断


继承与类型转换

向上转型(基类指针/引用接收派生类对象);protected/private继承阻断隐式向上转换,不再满足is‑a语义。

三种继承访问控制 & 向上隐式转换规则 表

继承方式 语法示例 派生类内部基类成员可见性 (基类public→派生类中) 派生类内部基类成员可见性 (基类protected→派生类中) 派生类内部基类成员可见性 (基类private→派生类中) 派生类对象 → 基类指针/引用 隐式向上转型 解读/注释
public公有继承 class Derived : public Base public protected 不可见 支持隐式向上转型 满足经典is‑a 语义;派生类对象就是一种基类对象;外部可以直接 Base* p = &d;;是多态的前提条件。
protected保护继承 class Derived : protected Base protected protected 不可见 禁止隐式向上转型 可强制static_cast,语义不是is‑a 基类public全部降级为protected;仅派生类、派生类的子类内部可以访问基类成员;外部不能隐式转基类指针;表达"实现复用",不是is‑a关系。
private私有继承 class Derived : private Base private private 不可见 禁止隐式向上转型 可强制static_cast,语义不是is‑a 基类全部成员降级为派生类private;仅本派生类内部可用,派生类的子类也无法访问基类成员;纯粹为复用基类实现,完全没有is‑a。
  • 向上转型隐式转换的本质 :C++语法规则:只有public继承才代表派生类是一种基类,编译器自动允许隐转换;protected/private继承代表"复用基类代码",不是is‑a,编译器直接禁止隐式转换。
  • static_cast<Base*>(&derived)强制转换语法上可以编译通过,属于绕过继承访问权限,但不代表is‑a,业务上不要这么写;private/protected继承设计本意就不是用来做向上转型。

继承与多态

三种继承与多态支持情况 表

继承方式 是否支持多态(虚函数) 推荐程度 语义关系 关键说明、注意事项 典型使用场景
public公有继承 ✅支持多态 ⭐强烈推荐 is‑a(是一个) 只有public继承,虚函数才能发挥多态效果;允许隐式向上转型;面向对象接口继承。 接口抽象、虚函数多态、基类指针管理派生类对象。
protected保护继承 ⚠️语法上虚函数仍存在,但不能外部多态调用 ⚠️不推荐 has‑a(复用实现) 外部无法隐式向上转型,无法通过基类指针触发多态;仅派生类及其子类内部可以使用基类功能。 极少使用;需要让派生类及其子类复用基类实现,对外屏蔽基类接口。
private私有继承 ❌不支持对外多态 ❌不推荐 has‑a(复用实现) 虚函数仅在派生类内部可见,外部无法通过基类指针调用多态行为;等价于组合的一种替代方案。 复用基类代码实现,但完全对外隐藏基类所有接口;优先使用**组合(包含对象成员)**替代私有继承。

定位new(placement new)

cpp 复制代码
char buf[100];
T* p = new (buf) T(10);
// Only construct object on buf, no memory allocation

定位new(placement new),C++98 标准就已经存在。

  • English: Placement new does NOT allocate any memory; it only invokes the constructor of T on the memory address you provide (here the address stored in finish).
    中文:定位new不分配任何内存,它只在你给定的内存地址(此处是finish保存的地址)上调用T的构造函数。

arr decays to int*

cpp 复制代码
int arr[10] = {1,2,3};

int (*arrx2)[10] = arr;//err

int (*arrx2)[10] = &arr;//correct form

用数组指针强转,可以将退化的数组指针 重新指向整个数组

相关推荐
不会代码的小猴1 小时前
2. 了解Qt
开发语言·c++·笔记·qt·算法
兵哥工控1 小时前
mfc实现在数值范围内实时改变静态文本控件字体颜色实例
c++·mfc
程序喵大人3 小时前
【C++进阶】STL算法与函数对象 - 09 函数对象保存状态并复用规则
开发语言·c++·算法·stl·函数对象
ShineWinsu9 小时前
对于C++:auto_ptr、unique_ptr、shared_ptr的模拟实现
c++·面试·笔试·智能指针·unique_ptr·shared_ptr·auto_ptr
XLYcmy11 小时前
京东 算法实习一面 下+手撕
c++·python·llm·概率论·数据处理·训练·codebert
反转180度12 小时前
Qt 6 + C++17 实现 SFTP 批量部署:工业设备运维工具实战解析
运维·c++·qt
码匠许师傅13 小时前
【C++ 面试真题】聊聊 C++ 的多继承与虚继承
开发语言·c++·面试
有点。15 小时前
C++认识数
开发语言·c++
2023自学中15 小时前
Qt 简易聊天室(第二篇),单线程 改为 多线程,方便后续增加文件传输功能
c++·qt