> 技术文档 > 横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!

横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!


横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!

  • 构 造 函 数
    • 函 数 体 内 赋 值
    • 初 始 化 列 表
      • 定 义
      • 规 则
  • explicit 关 键 字
  • static
    • 定 义
    • 特 性
  • 友 元 函 数
    • 定 义
    • 特 性
    • 类 型
  • 内 部 类
    • 定 义
    • 特 性
  • 匿 名 对 象
    • 定 义
    • 创 建 方 式
    • 生 命 周 期
  • 总 结

💻作 者 简 介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 数据 结 构。
💡个 人 主 页:@笑口常开xpr 的 个 人 主 页
📚系 列 专 栏:C++ 炼 魂 场:从 青 铜 到 王 者 的 进 阶 之 路

✨代 码 趣 语:匿 名 对 象 是 代 码 世 界 的 临 时 工,即 用 即 毁,无 需 命 名,像 流 星 般 划 过 代 码,高 效 完 成 临 时 任 务!
💪代 码 千 行,始 于 坚 持,每 日 敲 码,进 阶 编 程 之 路。
📦gitee 链 接:gitee

在这里插入图片描述

         C++ 类 与 对 象 的 核 心 扩 展 概 念 是 基 础 中 的 重 点。从 对 象 初 始 化 的 构 造 函 数,到 控 制 类 型 转 换 的 explicit;从 实 现 数 据 共 享 的 static 成 员,到 打 破 封 装 的 友 元 机 制,再 到 灵 活 使 用 的 内 部 类 与 匿 名 对 象,这 些 概 念 决 定 了 代 码 的 规 范 度 与 灵 活 性。本 文 通 过 实 例 解 析,帮 你 快 速 掌 握 这 些 核 心 知 识 点。


构 造 函 数

函 数 体 内 赋 值

#include <iostream>#include <assert.h>using namespace std;class Date{public://构造函数//函数体内赋值Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << \"-\" << _month << \"-\" << _day << endl;}private:int _year;int _month;int _day;};int main(){Date d1(2025, 7, 14);d1.Print();return 0;}

横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!
         虽 然 上 述 构 造 函 数 调 用 之 后,对 象 中 已 经 有 了 一 个 初 始 值,但 是 不 能 将 其 称 为 对 对 象 中 成 员 变 量 的 初 始 化,构 造 函 数 体 中 的 语 句 只 能 将 其 称 为 赋 初 值,而 不 能 称 作 初 始 化。因 为 初 始 化 只 能 初 始 化 一 次,而 构 造 函 数 体 内 可 以 多 次 赋 值。

初 始 化 列 表

定 义

         初 始 化 列 表 位 于 构 造 函 数 的 参 数 列 表 之 后、函 数 体 之 前,以 一 个 冒 号 开 始,接 着 是 一 个 以 逗 号 分 隔 的 数 据 成 员 列 表,每 个 “成 员 变 量” 后 面 跟 一 个 放 在 括 号 中 的 初 始 值 或 表 达 式。

规 则

(1)每 个 成 员 变 量 在 初 始 化 列 表 中 最 多 只 能 出 现 一 次(初 始 化 只 能 初 始 化 一 次)。

(2)类 中 包 含 以 下 成 员,必 须 放 在 初 始 化 列 表 位 置 进 行 初 始 化:引 用 成 员 变 量、const 成 员 变 量、自 定 义 类 型 成 员 (且 该 类 没 有 默 认 构 造 函 数 时)。 其 中,const 和 引 用 的 共 同 点 是 在 定 义 时 必 须 初 始 化。

(3)常 量 成 员(const):常 量 一 旦 赋 值 就 不 能 修 改,必 须 在 初 始 化 时 赋 值。
引 用 成 员(&):引 用 必 须 在 定 义 时 绑 定 到 对 象。
没 有 默 认 构 造 函 数 的 自 定 义 类 型 成 员:若 类 的 成 员 是 另 一 个 类 类 型,且 该 类 没 有 默 认 构 造 函 数,则 必 须 通 过 初 始 化 列 表 显 式 调 用 其 带 参 构 造 函 数。默 认 构 造 函 数 是 指 编 译 器 生 成 的,全 是 缺 省 值 的,没 有 参 数 的 构 造 函 数。

(4)在 初 始 化 列 表 中 不 能 出 现 相 同 的 成 员。

#include<iostream>using namespace std;class A{public:A(int a = 0):_a(a){//函数体cout << \"A(int a = 0)\" << endl;}private:int _a;};class B{public://初始化列表,对象成员定义的位置B(int a, int& ref): _ref(ref), _n(1), _x(2),_aobj(10){}private:A _aobj;//自定义类型int& _ref;//引用const int _n;//constint _x = 1; //内置类型};int main(){//对象整体定义int n = 10;B bb(10, n);return 0;}

横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!
注 意

  1. A 是 类,也 是 自 定 义 类 型,A 的 类 中,public 里 面 是 初 始 化 列 表,当 参 数 中 没 有 缺 省 值 时,必 须 传 参,必 须 使 用 B 的 初 始 化 列 表 进 行 传 参。_aobj(10) 这 是 给 A 传 参,参数是10。
  2. A 和 B 的 private 中 是 声 明,int _x = 1; 这 句 代 码 的 1 也 是 缺 省 值,用 来 初 始 化 变 量 _x。
  3. 在 B 的 初 始 化 列 表 中,a 和 ref 可 以 使 用 也 可 以 不 使 用,如 果 给 引 用 初 始 化,传 的 是 变 量,而 不 是 整 数。

(5)初 始 化 列 表 是 构 造 函 数 的 一 部 分,不 能 代 替 构 造 函 数,可 以 代 替 函 数 体 内 赋 值。建 议 使 用 初 始 化 列 表 初 始 化,初 始 化 列 表 可 以 为 构 造 函 数 传 参。

(6)初 始 化 列 表 无 法 做 完 所 有 的 事 情,所 有 的 成 员 变 量 都 会 经 过 初 始 化 列 表。

(7)尽 量 使 用 初 始 化 列 表 初 始 化,因 为 不 管 你 是 否 使 用 初 始 化 列 表,对 于 自 定 义 类 型 成 员 变 量,一 定 会 先 使 用 初 始 化 列 表 初 始 化。

(8)成 员 变 量 在 类 中 声 明 次 序 就 是 其 在 初 始 化 列 表 中 的 初 始 化 顺 序,与 其 在 初 始 化 列 表 中 的 先 后 次 序 无 关。函 数 体 内 是 按 顺 序 执 行 的。建 议 初 始 化 列 表 中 定 义 的 顺 序 和 声 明 的 顺 序 保 持 一 致。

#include<iostream>using namespace std;class A{public:A(int a): _a1(a), _a2(_a1){}void Print() {cout << _a1 << \" \" << _a2 << endl;}private:int _a2;int _a1;};int main() {A aa(1);aa.Print();return 0;}

横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!
         如 果 将 private 中 先 写_a1,再 写_a2,程 序 输 出 结 果 是 1 1。

explicit 关 键 字

         禁 止 使 用 类 型 转 换,explicit 添 加 在 构 造 函 数 的 前 面。

#include<iostream>using namespace std;class A{public:A(int a):_a(a){cout << \"A(int a)\" << endl;}A(const A& aa):_a(aa._a){cout << \"A(const A& aa)\" << endl;}private:int _a;};int main(){A aa1(1);A aa2 = 2;return 0;}

横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!
         在 上 面 的 代 码 中 aa1 是 使 用 默 认 构 造 函 数 初 始 化,aa2 是 隐 式 类 型 转 换。使 用 2 调 用 构 造 函 数 构 造 1 个 A 的 临 时 对 象,临 时 对 象 再 拷 贝 构 造 由 整 型 转 换 成 自 定 义 类 型。

         如 果 在 A aa2 = 2; 中 加 上 引 用,代 码 会 报 错,是 因 为 隐 式 类 型 转 换 会 产 生 临 时 变 量,临 时 变 量 具 有 常 性,前 面 必 须 加 上 const 修 饰。

         如 果 想 要 禁 止 类 型 转 换,可 以 在 构 造 函 数 前 面 加 上explicit。

static

定 义

         声 明 为 static 的 类 成 员 称 为 类 的 静 态 成 员,用 static 修 饰 的 成 员 变 量,称 之 为 静 态 成 员 变 量,静 态 成 员 变 量 存 储 在 静 态 区,不 在 函 数 的 栈 帧 里 面。用 static 修 饰 的 成 员 函 数,称 之 为 静 态 成 员 函 数。

特 性

  1. 成 员 变 量 属 于 每 一 个 类 对 象,存 储 在 对 象 里 面。静 态 成 员 变 量 属 于 类,属 于 类 的 每 个 对 象 共 享,存 储 在 静 态 区,生 命 周 期 是 全 局 的。

  2. 静 态 成 员 变 量 不 能 在 初 始 化 列 表 中 定 义,静 态 成 员 变 量 一 定 要 在 类 外 进 行 初 始 化,定 义 时 突 破 了 访 问 限 定 符 的 限 制。但 是 在 函 数 内 部 访 问 时 没 有 突 破 访 问 限 定 符 的 限 制,可 以 使 用 公 有 的 成 员 函 数 进 行。

#include<iostream>using namespace std;class MyClass {public: static int count; //声明静态成员变量};//类外初始化,初始值设为0int MyClass::count = 0;
  1. 静 态 成 员 函 数 没 有 this 指 针,指 定 类 域 和 访 问 限 定 符 就 可 以 访 问,静 态 成 员 函 数 和 静 态 成 员 变 量 一 般 是 一 起 出 现 的。
    访 问 方 式
int main() { // 方式一:通过类名访问 MyClass::count = 10; // 方式二:通过对象访问 MyClass obj1, obj2; obj1.count = 20; // 实际上修改的是同一个count cout << obj2.count << endl; // 输出20}
  1. 静 态 成 员 变 量 不 能 在 声 明 时 初 始 化,静 态 成 员 变 量 没 有 初 始 化 列 表。成 员 变 量 可 以 在 声 明 时 初 始 化,有 初 始 化 列 表 初 始 化。

注 意
         静 态 成 员 函 数 中 不 能 访 问 非 静 态 的,因 为 没 有 this 指 针。非 静 态 的 成 员 函 数 可 以 访 问 静 态 的 成 员 函 数。

#include<iostream>using namespace std;class MyClass {public: void Func() { count++; Mcount(); } static int Mcount() { count++; //Func(); return count; }private: static int count; };int MyClass::count = 0;int main() { MyClass a1; cout << a1.Mcount() << endl; return 0;}

横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!

友 元 函 数

定 义

         友 元 函 数 是 一 种 特 殊 的 函 数,它 不 是 类 的 成 员 函 数,友 元 函 数 相 当 于 类 的 朋 友,能 够 访 问 该 类 的 私 有 和 保 护 成 员。
         友 元 函 数 需 要 在 类 的 内 部 进 行 声 明,声 明 时 要 使 用 friend 关 键 字。它 可 以 是 普 通 函 数,也 可 以 是 其 他 类 的 成 员 函 数。

#include<iostream>using namespace std;class MyFriend{public: friend void friendFunc(MyFriend obj); // 声明友元函数private: int a = 0; int b = 0;};//友元函数的定义(不需要friend关键字)void friendFunc(MyFriend obj){ obj.a = 10; //可以访问私有成员 cout << obj.a << endl;}int main(){ MyFriend a1; friendFunc(a1); return 0;}

横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!

特 性

访 问 权 限:友 元 函 数 能 够 访 问 类 的 私 有、保 护 和 公 有 成 员。
非 成 员 函 数:友 元 函 数 不 属 于 类,因 此 没 有 this 指 针。
声 明 位 置:友 元 函 数 的 声 明 可 以 放 在 类 的 任 意 位 置(公 有、私 有 或 保 护 部 分),效 果 都 是 一 样 的。

类 型

普 通 友 元 函 数

#include<iostream>using namespace std;class Point {private: int x, y;public: Point(int a = 1, int b = 2) : x(a) , y(b) {} friend int distance(Point p1, Point p2); // 普通友元函数};//计算两点之间的距离int distance(Point p1, Point p2) { return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));}int main(){ Point p1(1, 2); Point p2(2, 3); int ret = distance(p1, p2); cout << ret << endl; return 0;}

横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!
类 的 友 元 函 数

#include<iostream>using namespace std;class A;class B{public: void func(A a);};class A {public: friend void B::func(A a); //声明B的成员函数为友元private: int data = 0;};void B::func(A a){ a.data = 100; cout << a.data << endl;}int main(){ A a; B b; b.func(a); return 0;}

横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!
友 元 类
         友 元 类 可 以 访 问 类 中 的 私 有 成 员。

#include<iostream>using namespace std;class A {private: int data = 0;public: friend class B; //声明B为友元类};class B {public: void setData(A& a, int value) { a.data = value; //可以访问A的私有成员 cout << a.data << endl; }};int main(){ int value = 10; A a; B b; b.setData(a, value); return 0;}

横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!

内 部 类

定 义

         如 果 一 个 类 定 义 在 另 一 个 类 的 内 部,这 个 内 部 类 就 叫 做 内 部 类。内 部 类 是 一 个 独 立 的 类,它 不 属 于 外 部 类,更 不 能 通 过 外 部 类 的 对 象 去 访 问 内 部 类 的 成 员。外 部 类 对 内 部 类 没 有 任 何 优 越 的 访 问 权 限。

注 意
         内 部 类 就 是 外 部 类 的 友 元 类,参 见 友 元 类 的 定 义,内 部 类 可 以 通 过 外 部 类 的 对 象 参 数 来 访 问 外 部 类 中 的 所 有 成 员。但 是 外 部 类 不 是 内 部 类 的 友 元。

特 性

  1. 内 部 类 可 以 定 义 在 外 部 类 的 public、protected、private 都 是 可 以 的。
  2. 注 意 内 部 类 可 以 直 接 访 问 外 部 类 中 的 static 成 员,不 需 要 外 部 类 的 对 象 / 类 名。
  3. sizeof(外 部 类) = 外 部 类,和 内 部 类 没 有 任 何 关 系。
#include<iostream>using namespace std;class A{public://B在A中不占空间,对象占空间class B //B天生就是A的友元{public:void foo(const A& a){cout << k << endl;cout << a.h << endl;}private:int b;};private:static int k;int h;};int A::k = 1;int main(){cout << sizeof(A) << endl;A::B b;b.foo(A());return 0;}

横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!
         静 态 成 员 变 量 属 于 类 本 身,而 不 是 类 的 对 象。由 于 静 态 成 员 变 量 不 存 储 在 类 的 对 象 中,因 此 sizeof 计 算 类 的 大 小 时 不 会 包 含 静 态 成 员 变 量 的 大 小。所 以 sizeof(A) 只 有 h 的 大 小。

匿 名 对 象

定 义

         匿 名 对 象 是 一 种 特 殊 的 对 象,它 没 有 名 称,是 在 创 建 对 象 时 直 接 使 用 类 名 进 行 实 例 化 的 对 象,通 常 用 于 临 时 的 操 作。

创 建 方 式

#include<iostream>using namespace std;class MyClass {public: MyClass() { cout << \"MyClass()\" << endl; } MyClass(int num) { cout << \"MyClass(int num):\" << num << endl; } void Func(int n) { cout << n << endl; }};int main(){ MyClass my;//有名对象 my.Func(1); //创建无参匿名对象 MyClass(); //创建带参匿名对象 MyClass(10); MyClass().Func(1); return 0;}

横 扫 C++ 核 心 概 念:初 始 化 列 表 + static + 友 元 + 内 部 类,轻 松 碾 压 面 试 难 题!
         在 MyClass().Func(1); 中 空 括 号 表 示 使 用 默 认 构 造 函 数 创 建 对 象。如 果 类 没 有 默 认 构 造 函 数(即 没 有 无 参 构 造 函 数),或 者 需 要 使 用 带 参 数 的 构 造 函 数,则 必 须 在 括 号 中 传 递 相 应 的 参 数。

生 命 周 期

         有 名 对 象 的 生 命 周 期 在 当 前 函 数 的 局 部 域。匿 名 对 象 的 生 命 周 期 比 较 短 暂,当 包 含 它 的 语 句 执 行 结 束 后, 该 匿 名 对 象 就 会 被 自 动 销 毁,调 用 其 析 构 函 数。

在这里插入图片描述


总 结

         本 文 讲 解 了 C++ 类 与 对 象 的 关 键 扩 展 概 念:构 造 函 数 的 初 始 化 列 表 是 引 用、const 等 成 员 的 必 选 初 始 化 方 式,且 初 始 化 顺 序 由 声 明 顺 序 决 定;explicit 可 禁 止 单 参 构 造 函 数 的 隐 式 转 换;static 成 员 属 于 类,支 持 数 据 共 享;友 元 机 制 允 许 类 外 访 问 私 有 成 员,但 需 慎 用;内 部 类 不 影 响 外 部 类 大 小,是 其 天 然 友 元;匿 名 对 象 适 合 临 时 操 作。这 些 概 念 平 衡 了 封 装 性 与 灵 活 性,掌 握 它 们 不 仅 能 应 对 面 试,更 能 为 编 写 高 质 量 C++ 代 码 打 下 基 础。