
适合人群:已掌握默认四大函数(构造/析构/拷贝/赋值)的 C++ 初学者 核心目标:掌握初始化列表、static 成员、友元等高频考点,理解编译器优化机制,写出工业级规范代码!
我们已经走过了 C++ 类与对象的地基(封装)与骨架(生命周期管理),现在,是时候为它注入高级灵魂了!
本篇将系统讲解《04.类和对象(下).pdf》中的全部内容,包括:
✅ 初始化列表:为什么它是构造函数的“唯一正解”? ✅ explicit:如何防止隐式转换引发的逻辑灾难? ✅ static 成员:实现对象计数器、巧解“求 1+2+...+n” ✅ 友元(friend):何时需要“打破封装”? ✅ 内部类:封装紧密耦合逻辑的利器 ✅ 匿名对象:临时对象的优雅写法 ✅ 编译器优化:你的拷贝,可能根本没发生!
那我们就开始吧!
🧩 什么是初始化列表? 初始化列表以冒号开头,后跟逗号分隔的成员初始化列表,每个成员后跟括号中的初始值或表达式:
Date(int& x, int year, int month, int day)
: _year(year), _month(month), _day(day), _t(12), _ref(x), _n(1) {
// 函数体用于逻辑处理,**不用于初始化**
}int& _ref)→ 引用必须在定义时绑定const int _n)→ const 只能初始化,不能赋值Time _t)→ 编译器无法先默认构造再赋值class Time {
public:
Time(int hour) : _hour(hour) {}
private:
int _hour;
};
class Date {
public:
Date(int& x, int year = 1, int month = 1, int day = 1)
: _year(year), _month(month), _day(day), _t(12), _ref(x), _n(1) {}
private:
int _year, _month, _day;
Time _t; // 没有默认构造
int& _ref; // 引用
const int _n; // const
};❌ 若不用初始化列表,编译器报错:
error C2512: “Time”: 没有合适的默认构造函数可用error C2530: “Date::_ref”: 必须初始化引用与初始化列表书写顺序无关! 严格按类中声明顺序初始化。
class A {
int _a2 = 2;
int _a1 = 2;
public:
A(int a) : _a1(a), _a2(_a1) {}
void Print() { cout << _a1 << " " << _a2 << endl; }
};
A aa(1); aa.Print(); // 输出:1 随机值!原因:先初始化
_a2(此时_a1未初始化!)💡 建议:声明顺序 = 初始化列表顺序,避免陷阱。
class Date {
int _year = 1900;
Time _t = 12; // 调用 Time(12)
const int _n = 1;
int* _ptr = (int*)malloc(12);
public:
Date() : _month(2) {} // 未初始化的成员用缺省值
void Print() const { cout << _year << "-" << _month << "-" << _day << endl; }
private:
int _month = 1, _day;
};✅ 缺省值本质是“初始化列表的后备方案”,优先级:显式初始化 > 成员缺省值 > 默认行为
C++ 允许用单参数构造函数进行隐式转换:
class A {
public:
A(int a1) : _a1(a1) {}
private:
int _a1 = 1, _a2 = 2;
};
A aa1 = 1; // 隐式转换:1 → A(1)explicit 禁止隐式转换:explicit A(int a1) : _a1(a1) {}
// A aa1 = 1; // ❌ 编译错误
A aa1(1); // ✅ 必须显式构造A aa3 = {2, 2}; // 需要构造函数支持💡 建议:所有单参数构造函数默认加上 explicit,除非明确需要隐式转换(如 string s = "hello")
this 指针,只能访问静态成员类名::成员 或 对象.成员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; // 0
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl; // 3
}class Sum {
public:
Sum() { _ret += _i; ++_i; }
static int GetRet() { return _ret; }
private:
static int _i, _ret;
};
int Sum::_i = 1, _ret = 0;
class Solution {
public:
int Sum_Solution(int n) {
Sum arr[n]; // 利用数组构造 n 次,自动累加
return Sum::GetRet();
}
};class B; // 前置声明
class A {
friend void func(const A& aa, const B& bb);
private:
int _a1 = 1, _a2 = 2;
};
class B {
friend void func(const A& aa, const B& bb);
private:
int _b1 = 3, _b2 = 4;
};
void func(const A& aa, const B& bb) {
cout << aa._a1 << endl; // 访问 A 私有
cout << bb._b1 << endl; // 访问 B 私有
}class A {
friend class B;
private:
int _a1 = 1, _a2 = 2;
};
class B {
public:
void func1(const A& aa) {
cout << aa._a1 << endl; // 可访问 A 私有
}
private:
int _b1 = 3, _b2 = 4;
};将一个类定义在另一个类内部,称为内部类。
sizeof(外部类) 不包含内部类)class A {
private:
static int _k;
int _h = 1;
public:
class B { // 内部类,自动是 A 的友元
public:
void foo(const A& a) {
cout << _k << endl; // 访问 static
cout << a._h << endl; // 访问非 static
}
int _b1;
};
};
int A::_k = 1;
int main() {
cout << sizeof(A) << endl; // 输出 4(仅 _h),不含 B
A::B b;
A aa;
b.foo(aa);
}class Solution {
class Sum {
public:
Sum() { _ret += _i; ++_i; }
};
static int _i, _ret;
public:
int Sum_Solution(int n) {
Sum arr[n];
return _ret;
}
};
int Solution::_i = 1, _ret = 0;用 类型(参数) 直接创建对象,不命名,称为匿名对象。
A(); // 构造后立即析构
A(1); // 带参匿名对象
Solution().Sum_Solution(10); // 调用后立即销毁✅ 优势:避免命名污染、简化临时逻辑
现代编译器会自动优化不必要的拷贝(如 RVO/NRVO):
A f() { A aa; return aa; }
A aa2 = f(); // 理论:构造 → 拷贝 → 拷贝
// 实际:直接构造 aa2(无拷贝!)-fno-elide-constructorsclass A {
public:
A(int a = 0) : _a1(a) { cout << "A(int a)" << endl; }
A(const A& aa) : _a1(aa._a1) { cout << "A(const A& aa)" << endl; }
A& operator=(const A& aa) {
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa) _a1 = aa._a1;
return *this;
}
~A() { cout << "~A()" << endl; }
private:
int _a1 = 1;
};
void f1(A aa) {}
A f2() { A aa; return aa; }
int main() {
A aa1;
f1(aa1); // 构造 + 拷贝构造
f1(1); // 隐式转换优化为直接构造
f1(A(2)); // 连续构造优化为一个构造
f2(); // 返回优化
A aa2 = f2(); // 跨对象优化
aa1 = f2(); // 无法完全优化(涉及赋值)
}💡 重要启示: 即使你写了拷贝构造,也不代表它一定会被调用! 但仍需正确实现——因为优化不是强制的。

class A {
int _a2 = 2;
int _a1 = 2;
public:
A(int a) : _a1(a), _a2(_a1) {}
void Print() { cout << _a1 << " " << _a2 << endl; }
};
A aa(1); aa.Print(); // ?答案我就放在评论区了
至此,《C++ 类与对象》三部曲圆满收官! 我们从封装与构造,到生命周期管理,再到高级特性与工程实践, 构建了完整的 C++ 面向对象知识体系。
但请记住:这只是 C++ 的冰山一角。 真正的力量,始于继承与多态——那将是我们的新征途。
“掌握对象,方能驾驭系统;理解封装,才能设计优雅。”
如果本文对你有帮助,欢迎点赞 ❤️ 收藏 ⭐ +关注,支持一下我,Thanks♪(・ω・)ノ