跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 类和对象进阶:初始化列表、static 成员与编译器优化

C++ 类和对象进阶涵盖初始化列表、static 成员、构造析构顺序及编译器优化。初始化列表用于 const、引用及无默认构造函数成员的初始化,遵循声明顺序而非书写顺序。static 成员共享存储于静态区,需类外初始化且无 this 指针。全局、局部及静态对象的生命周期遵循特定构造与析构规则。友元打破封装限制,内部类提供极致封装。匿名对象减少临时开销。现代编译器通过 RVO/NRVO 优化拷贝构造,提升传值返回性能。掌握这些机制有助于编写高效、安全的 C++ 代码。

活在当下发布于 2026/3/30更新于 2026/9/1062 浏览
C++ 类和对象进阶:初始化列表、static 成员与编译器优化

C++ 类和对象进阶:初始化列表、static 成员与编译器优化

一、再探构造函数:初始化列表

在构造函数体内给成员变量赋值,严格来说这只能叫函数体内赋初值。对象实例化时,内存分配后成员变量已经存在。进入构造函数 {} 内部是对已存在变量的二次赋值。

遇到以下三种成员变量时,必须在定义瞬间初始化,否则编译报错:

  1. const 成员变量:常量一旦定义,值锁定。
  2. 引用成员变量:引用定义时必须绑定实体。
  3. 无默认构造函数的自定义类型成员:必须显式指定构造方式。

1. 语法格式

以冒号开始,逗号分隔的成员列表,每个成员后跟初始值或表达式。

class A {
public:
    // 初始化列表才是成员变量真正定义(出生)的地方!
    A(int a, int ref) : _a1(a), _n(10), _ref(ref) {}
private:
    int _a1;
    const int _n;      // 必须走列表
    int& _ref;         // 必须走列表
};

2. 规则与注意事项

  • 唯一性:每个成员变量在初始化列表中只能出现一次。
  • 强制初始化:引用、const、无默认构造函数的自定义类型必须在初始化列表中完成。
  • 执行顺序:仅由成员变量在类中的声明顺序决定,与初始化列表书写顺序无关。建议保持声明顺序与列表顺序一致。
  • C++11 缺省值:支持在成员声明处设置缺省值,供未在初始化列表中显式初始化的成员使用。

3. 初始化顺序陷阱

class A {
public:
    A(int a) : _a1(a), _a2(_a1) {} // 踩坑点
    void Print() { cout << _a1 << " " << _a2 << endl; }
private:
    int _a2; // 先声明
    int _a1; // 后声明
};
int main() {
    A obj(1);
    obj.Print();
    return 0;
}

结果:输出 1 和一个随机垃圾值。 原因:_a2 先声明,先被初始化。此时 _a1 尚未初始化,值为随机。随后才初始化 _a1。

二、隐式类型转换与 explicit 关键字

单参数(或除第一个参数外其余都有默认值)的构造函数承担隐式类型转换作用。

class A {
public:
    A(int a) : _a(a) {}
private:
    int _a;
};
int main() {
    A obj1(2);       // 正常构造
    A obj2 = 3;      // 发生隐式类型转换
    return 0;
}

为禁止隐式转换,可在构造函数前加 explicit 关键字。

三、static 成员与面试题拆解

静态成员变量为所有类对象共享,不属于具体对象,存放在静态区,不计入 sizeof(对象)。

1. static 成员特性

  • 存储:静态区,类外初始化。
  • this 指针:静态成员函数没有 this 指针,不能访问非静态成员。
  • 访问方式:推荐 类名::静态成员。
#include <iostream>
using namespace std;

class A {
public:
    A() { ++_scount; }
    A(const A& t) { ++_scount; }
    ~A() { --_scount; }
    static int GetACount() { return _scount; }
private:
    static int _scount;
};
// 静态成员变量必须类外初始化
int A::_scount = 0;

int main() {
    cout << A::GetACount() << endl;
    A a1, a2;
    A a3(a1);
    cout << A::GetACount() << endl;
    return 0;
}

2. 高频题:求 1+2+...+n

题目限制:不能使用乘除法、for、while、if、else、switch、case 等。 思路:利用对象生命周期,实例化 n 个对象触发 n 次构造函数累加。

class Sum {
public:
    Sum() { _ret += _i; ++_i; }
    static int GetRet() { return _ret; }
private:
    static int _i;
    static int _ret;
};
int Sum::_i = 1;
int Sum::_ret = 0;

class Solution {
public:
    int Sum_Solution(int n) {
        Sum arr[n];
        return Sum::GetRet();
    }
};

四、全局、局部、静态对象的构造与析构顺序

假设代码:

C c;
int main() {
    A a;
    B b;
    static D d;
    return 0;
}
  • 构造顺序:全局对象最先(main 前),局部对象按顺序(main 内),静态局部对象首次执行到语句时。
    • 结论:C -> A -> B -> D
  • 析构顺序:后构造的先析构(栈 LIFO)。局部对象先销毁,静态/全局对象最后销毁。
    • 结论:B -> A -> D -> C

五、友元:打破封装的'合法后门'

友元分为友元函数和友元类,允许外部访问私有成员。

1. 友元函数

class A {
    friend void func(const A& aa);
    int _a1 = 1;
};
void func(const A& aa) {
    cout << aa._a1 << endl;
}

2. 友元类

class A {
    friend class B;
    int _a1 = 1;
};
class B {
public:
    void PrintA(const A& aa) {
        cout << aa._a1 << endl;
    }
};

注意:友元具有单向性和不可传递性。工程中慎用,避免破坏封装。

六、内部类

内部类是定义在另一个类内部的类,完全独立,不影响外部类大小。

  • 权限:内部类默认是外部类的友元,可访问外部类所有成员。
  • 专属内部类:将内部类置于 private 区域,成为外部类专属。
class Solution {
private:
    class Sum {
    public:
        Sum() { _ret += _i; ++_i; }
    };
    static int _i;
    static int _ret;
public:
    int Sum_Solution(int n) {
        Sum arr[n];
        return _ret;
    }
};
int Solution::_i = 1;
int Solution::_ret = 0;

七、匿名对象和有名对象

  • 有名对象:类型 对象名 (实参),生命周期贯穿作用域。
  • 匿名对象:类型 (实参),生命周期仅限当前行。
// 高级写法
Solution().Sum_Solution(10);

八、对象拷贝时的编译器优化

现代编译器会优化传值和返回时的拷贝开销。

场景 A:参数传递优化

void f1(A aa) {}
int main() {
    f1(1);       // 优化为直接构造
    f1(A(2));    // 优化为直接构造
    return 0;
}

场景 B:传值返回优化 (RVO/NRVO)

A f2() {
    A aa;
    return aa;
}
int main() {
    A obj = f2();
    return 0;
}
  • 无优化:1 次构造 + 2 次拷贝。
  • NRVO 优化:直接在外部 obj 空间构造,0 次拷贝,仅 1 次构造。 建议:若必须传值返回,放心交给编译器优化,无需手动规避。

目录

  1. C++ 类和对象进阶:初始化列表、static 成员与编译器优化
  2. 一、再探构造函数:初始化列表
  3. 1. 语法格式
  4. 2. 规则与注意事项
  5. 3. 初始化顺序陷阱
  6. 二、隐式类型转换与 explicit 关键字
  7. 三、static 成员与面试题拆解
  8. 1. static 成员特性
  9. 2. 高频题:求 1+2+...+n
  10. 四、全局、局部、静态对象的构造与析构顺序
  11. 五、友元:打破封装的“合法后门”
  12. 1. 友元函数
  13. 2. 友元类
  14. 六、内部类
  15. 七、匿名对象和有名对象
  16. 八、对象拷贝时的编译器优化
  17. 场景 A:参数传递优化
  18. 场景 B:传值返回优化 (RVO/NRVO)

更多推荐文章

查看全部
  • 使用 MybatisPlus 将百度天气数据持久化至 PostgreSQL
  • Quartus 18.0 安装及 ModelSim 环境配置指南
  • Linux 从入门到实践:CentOS 服务器配置与用户管理指南
  • M2FP模型在VR社交中的身体表达技术
  • 前端 CI/CD 流程:构建与自动化部署最佳实践
  • 使用 copilot-api 实现 GitHub Copilot 兼容 OpenAI 与 Anthropic 生态
  • Git 基础指令与本地仓库操作指南
  • 基于 FPGA 与 W5500 的 SPI 以太网通信实现
  • 2025 前端技术复盘:框架收敛、AI 赋能与开发者生存指南
  • Python 开源 AI 模型引入与测试全流程实战
  • 如何自定义 Subway 图标:源文件编辑与颜色调整
  • 超越 import 与 pip:深入 Python 模块化与依赖管理核心
  • Qwen3-4B-Instruct 技术架构与 CPU 推理优化解析
  • 基于 Trae 部署与配置 Claude Code 实践
  • 机器人运动学:标准 DH 与改进 DH 参数对比
  • OpenCode AI 编程助手高级配置详解
  • AMD 显卡 llama.cpp Vulkan 后端兼容与优化指南
  • Windows 安装 Docker Desktop 详细指南
  • PyCharm 中 Copilot 插件 Claude 模型缺失的解决方案
  • 宇树 G1 机器人 FAST-LIO 建图与 RViz 配置教程

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online