
个人主页:
wengqidaifeng
✨ 永远在路上,永远向前走
个人专栏:
数据结构
C语言
嵌入式小白启动!
重要OJ算法题详解
蓝桥杯备战
C++从菜鸟到强手
python启航
文章目录
-
- 前言
- 一、初始化列表
-
- [1.1 什么是初始化列表](#1.1 什么是初始化列表)
- [1.2 哪些成员必须用初始化列表初始化](#1.2 哪些成员必须用初始化列表初始化)
- [1.3 初始化列表 vs 构造函数体内赋值](#1.3 初始化列表 vs 构造函数体内赋值)
- [1.4 C++11 成员变量默认值](#1.4 C++11 成员变量默认值)
- [1.5 成员变量初始化顺序](#1.5 成员变量初始化顺序)
- [二、explicit 关键字与隐式类型转换](#二、explicit 关键字与隐式类型转换)
-
- [2.1 隐式类型转换](#2.1 隐式类型转换)
- [2.2 隐式类型转换的场景](#2.2 隐式类型转换的场景)
- [2.3 用 explicit 禁止隐式转换](#2.3 用 explicit 禁止隐式转换)
- 三、友元(Friend)
-
- [3.1 友元函数](#3.1 友元函数)
- [3.2 友元类](#3.2 友元类)
- [四、static 成员](#四、static 成员)
-
- [4.1 static 成员变量](#4.1 static 成员变量)
- [4.2 static 成员函数](#4.2 static 成员函数)
- 五、内部类
- 六、匿名对象
- 七、流插入与流提取运算符重载
-
- [7.1 为什么不能是成员函数](#7.1 为什么不能是成员函数)
- [7.2 全局函数 + 友元方案](#7.2 全局函数 + 友元方案)
- 八、完整日期类的实现
- 总结
- 三篇回顾
前言
上篇介绍了类和对象的基础(类定义、this 指针、构造/析构函数),中篇讲解了核心难点(拷贝构造、深拷贝、赋值重载、运算符重载、const 成员)。本篇将覆盖类和对象中较为高级的特性:
- 初始化列表
- explicit 与隐式类型转换
- static 成员变量和成员函数
- 友元函数与友元类
- 内部类
- 匿名对象
- 流插入/流提取运算符重载
- 一个功能完备的日期类
一、初始化列表
1.1 什么是初始化列表
在此之前,我们都在构造函数体内做初始化:
cpp
Date(int year, int month, int day) {
_year = year; // 这不是"初始化",这是"赋值"
_month = month;
_day = day;
}
初始化列表是真正意义上的初始化,在进入构造函数体之前就完成了:
cpp
Date(int year, int month, int day)
: _year(year) // 初始化列表:用参数 year 初始化成员 _year
, _month(month)
, _day(day)
{
// 构造函数体,此时成员变量已经初始化完毕
}
语法 :在构造函数参数列表后加冒号 :,每个成员用逗号分隔,格式为 成员(初始值)。
1.2 哪些成员必须用初始化列表初始化
以下三类成员只能在初始化列表中初始化,不能在函数体内赋值:
1. const 成员变量
cpp
class Date {
public:
Date(int year)
: _n(1) // 正确:const 成员必须在初始化列表中初始化
{
// _n = 1; // 错误!const 变量不能赋值
}
private:
const int _n;
};
2. 引用成员变量
cpp
class Date {
public:
Date(int& xx)
: _ref(xx) // 正确:引用必须在初始化时绑定
{
// _ref = xx; // 错误!引用不能重新绑定
}
private:
int& _ref;
};
引用成员变量不常见,但了解即可。
3. 没有默认构造函数的类类型成员
cpp
class Time {
public:
Time(int hour) : _hour(hour) {} // 只有带参构造函数,无默认构造
private:
int _hour;
};
class Date {
public:
Date(int year, int month, int day)
: _year(year)
, _t(10) // 必须在初始化列表中调用 Time 的构造函数
{
// _t = Time(10); // 这是赋值,不是初始化(且多余一次构造)
}
private:
int _year, _month, _day;
Time _t; // Time 没有默认构造函数!
};
1.3 初始化列表 vs 构造函数体内赋值
cpp
// 初始化列表 → 直接调用成员的构造函数(一步到位)
Date(int year)
: _ptr((int*)malloc(12)) // 初始化列表
{
memset(_ptr, 0, 12); // 构造函数体内做额外处理
}
// 构造函数体赋值 → 先调用默认构造,再赋值(效率更低)
// 对于 int 等内置类型,两者效果相同
// 对于类类型成员,初始化列表直接调用目标构造函数,避免先默认再赋值的浪费
1.4 C++11 成员变量默认值
C++11 允许在声明成员变量时直接给默认值,它等价于初始化列表中的值:
cpp
class Date {
public:
Date(int year) : _year(year) {} // 只初始化 _year
// _month 取默认值 1,_day 取默认值 1
void Print() const {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year = 1; // C++11:成员变量默认值
int _month = 1;
int _day = 1;
int* _ptr = (int*)malloc(12); // 甚至可以初始化指针
Time _t = 1; // 隐式类型转换初始化类对象
};
优先级:初始化列表指定的值 > 声明时给定的默认值
1.5 成员变量初始化顺序
初始化的顺序与初始化列表中写的顺序无关,只与成员变量声明的顺序有关!
cpp
class A {
public:
A(int a = 0) : _a2(a), _a1(_a2) {} // 看起来先初始化 _a2,再 _a1
void Print() { cout << _a1 << " " << _a2 << endl; }
private:
int _a1; // 声明顺序:_a1 先于 _a2
int _a2;
};
int main() {
A aa; // 实际:_a1 先初始化,用到了还未初始化的 _a2(随机值)!
aa.Print(); // 输出:随机值 0
}
建议:初始化列表的顺序与变量声明顺序保持一致,避免潜在问题。
初始化顺序图解:
成员声明顺序
int _a1; ← 第1
int _a2; ← 第2
初始化列表书写顺序
: _a2(a), _a1(_a2)
← 这个顺序不重要!
实际执行顺序
① _a1 = _a2(此时_a2未初始化=随机值!)
② _a2 = a
⚠️ 结论:永远按声明顺序执行
与初始化列表书写顺序无关
二、explicit 关键字与隐式类型转换
2.1 隐式类型转换
如果一个构造函数只有单个参数(或有多个参数但有默认值),它可以充当隐式转换函数:
cpp
class A {
public:
A(int a = 0) : _a1(a) {}
void Print() { cout << _a1 << endl; }
private:
int _a1;
};
int main() {
A aa1(1);
aa1.Print(); // 输出:1
A aa2 = 2; // 隐式类型转换:先用 2 构造一个临时 A 对象,再拷贝给 aa2
aa2.Print(); // 输出:2
// 编译器优化后等价于:A aa2(2);
const A& raa2 = 2; // 临时对象可以绑定到 const 引用,延长生命周期
}
2.2 隐式类型转换的场景
cpp
class Stack {
public:
void Push(const A& aa);
private:
A _arr[10];
int _top;
};
int main() {
Stack st;
A aa(3);
st.Push(aa); // 正常传参
st.Push(3); // 隐式转换!3 → 构造临时 A → 传给 Push
// C++11 还支持多参数的隐式转换:
A aa5 = {1, 1}; // 多参数 → 列表初始化
const A& raa6 = {2, 2}; // 临时对象绑定到 const 引用
}
隐式类型转换链图解:
渲染错误: Mermaid 渲染失败: Parse error on line 2: ...TMP[构造临时对象
A tmp(3)] TMP -->|"2. -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'
2.3 用 explicit 禁止隐式转换
如果不希望发生意外的隐式转换,用 explicit 修饰构造函数:
cpp
class A {
public:
explicit A(int a = 0) : _a1(a) {}
};
int main() {
A aa1(1); // OK,显式调用
// A aa2 = 2; // 错误!explicit 禁止隐式转换
// st.Push(3); // 错误!
A aa2(2); // OK,显式调用
st.Push(A(3)); // OK,显式构造临时对象
}
经验 :建议给单参数构造函数加上
explicit,除非你明确想支持隐式转换。
三、友元(Friend)
友元打破了封装------允许外部函数或外部类访问本类的 private 成员。
3.1 友元函数
cpp
class Date {
// 声明全局函数 operator<< 为友元
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
// ...
private:
int _year, _month, _day;
};
// 现在这个全局函数可以直接访问 Date 的 private 成员了
ostream& operator<<(ostream& out, const Date& d) {
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
最常见的友元函数用途就是流插入(
<<)和流提取(>>)运算符重载(后面详细讲解)。
3.2 友元类
cpp
class A {
friend class B; // B 是 A 的友元类,B 可以访问 A 的所有私有成员
private:
int _a1 = 1;
int _a2 = 2;
};
class B {
public:
void func1(const A& aa) {
cout << aa._a1 << endl; // OK,B 是 A 的友元
}
void func2(const A& aa) {
cout << aa._a2 << endl; // OK
}
};
注意:
- 友元关系是单向的(B 能访问 A,不代表 A 能访问 B)
- 友元关系不能传递(A 是 B 的朋友,B 是 C 的朋友,A 不一定是 C 的朋友)
- 友元破坏了封装性,应谨慎使用
友元关系图解:
普通访问者
友元(Friend)------ 持有钥匙 🔑
Date 类 --- private 区域
可以访问
可以访问
可以访问
禁止
禁止
可以访问所有
_year
_month
_day
operator<< 全局函数
friend ostream& operator<<(...)
class B
friend class B
普通全局函数 ❌
其他类 ❌
Date成员函数 ✅
四、static 成员
4.1 static 成员变量
普通的成员变量属于每个对象,而 static 成员变量属于整个类,所有对象共享。
cpp
class A {
public:
A() { ++_scount; }
A(const A& t) { ++_scount; }
~A() { --_scount; }
static int GetACount() { return _scount; } // 静态成员函数
private:
static int _scount; // 声明静态成员变量(类内)
int _a = 1;
};
// 定义并初始化静态成员变量(类外,.cpp 中)
int A::_scount = 0;
int main() {
cout << A::GetACount() << endl; // 0------还没有任何对象
A a1, a2;
cout << A::GetACount() << endl; // 2
{
A a3(a1);
cout << A::GetACount() << endl; // 3
} // a3 析构
cout << A::GetACount() << endl; // 2
cout << sizeof(A) << endl; // 4(只有 int _a 的大小,不含 _scount)
}
关键规则:
- static 成员变量必须在类外定义(不是类内初始化,除非 C++17 inline static)
- static 成员变量存放在全局数据区(静态存储区) ,不计入
sizeof(类) - static 成员变量被所有对象共享
static 成员内存布局:
栈内存
全局数据区(静态存储区)
共享
共享
对象 a2
_a = 1
sizeof(a2) = 4
对象 a1
_a = 1
sizeof(a1) = 4
A::_scount = 2
← 只有一个副本,所有对象共享
sizeof(A) = 4
不含 static 成员
static GetACount()
✅ 可访问 _scount
❌ 没有 this,不能访问 _a
4.2 static 成员函数
cpp
class A {
public:
static int GetACount() {
// _a++; // 错误!static 函数中没有 this 指针,不能访问非静态成员
return _scount;
}
void func() {
cout << _scount << endl; // OK,普通函数可以访问 static 成员
}
private:
static int _scount;
int _a = 1;
};
static 成员函数没有 this 指针,因此不能访问非静态成员。反过来,普通成员函数可以访问 static 成员。
五、内部类
cpp
class A {
private:
static int _k;
int _h = 1;
public:
class B { // 内部类------B 默认是 A 的友元
public:
void foo(const A& a) {
cout << _k << endl; // OK,内部类访问外部类的 static 成员
cout << a._h << endl; // OK,内部类是外部类的友元,可以访问私有成员
}
private:
int _b = 1;
};
};
int A::_k = 1;
int main() {
cout << sizeof(A) << endl; // 4(只有 int _h,内部类不算 A 的大小)
A::B b; // 通过 A::B 使用内部类
A aa;
b.foo(aa);
}
内部类和外部类的大小是独立的。sizeof(A) 不会包含 B 的成员。
内部类与外部类关系图解:
class A(外部类)
class B(内部类)------ 天然是 A 的友元 🔑
A 的成员
✅ 通过 a._h 访问
✅ 直接访问 _k
static int _k
(类级别,全局数据区)
int _h = 1
(对象级别,栈/堆)
void foo(const A& a)
int _b = 1
sizeof(A) = 4
(只有 _h,不含内部类)
外部使用: A::B b;
内部类受 public/private 限制
六、匿名对象
匿名对象是没有名字的临时对象,生命周期只在当前行。
cpp
class A {
public:
A(int a = 0) : _a(a) {
cout << "A(int a)" << endl;
}
~A() {
cout << "~A()" << endl;
}
private:
int _a;
};
class Solution {
public:
int Sum_Solution(int n) {
return n;
}
};
int main() {
A aa1; // 有名字的对象:生命周期直到作用域结束
// A aa2(); // 错误!这是函数声明,不是匿名对象
A(); // 匿名对象:当前行结束即析构
A(1); // 匿名对象:参数为 1
// 匿名对象的典型应用场景------简化代码
Solution st;
cout << st.Sum_Solution(10) << endl;
// 用匿名对象,一行搞定:
cout << Solution().Sum_Solution(10) << endl;
// 另一个常见应用:给 sort 传入函数对象
int a[] = { 32, 71, 12, 45, 26, 80, 53, 33 };
sort(a, a + 8, greater<int>()); // 匿名 greater<int> 对象
}
匿名对象的生命周期延续规则:当匿名对象被 const 引用绑定时,生命周期延长到引用的生命周期。
cpp
const A& ref = A(100); // 匿名对象的生命周期延长到 ref 结束
七、流插入与流提取运算符重载
这是 C++ 中最常用的运算符重载之一,让自定义类型支持 cout 和 cin。
7.1 为什么不能是成员函数
cpp
class Date {
public:
// 错误思路:成员函数版本
void operator<<(ostream& out) {
out << _year << "年" << _month << "月" << _day << "日" << endl;
}
};
int main() {
Date d1;
// cout << d1; // 错误!左操作数是 cout,不是 Date
d1 << cout; // 只能用这种反直觉的写法!
// 等价于 d1.operator<<(cout);
}
原因:成员函数版本的左操作数必须是当前类的对象(this)。而 cout << d1 的左操作数是 cout(类型是 ostream),不是 Date。
一图看懂为什么流运算符必须是全局函数 + 友元:
成员函数无法实现
全局函数 + friend 实现
✅ 全局函数 + 友元
operator<<(cout, d1)
左操作数: cout
右操作数: d1
调用方式: cout << d1
符合直觉!
❌ 如果写成成员函数
d1.operator<<(cout)
左操作数: d1 (this)
右操作数: cout
调用方式: d1 << cout
完全反直觉!
需求: cout << d1
左操作数是 ostream 类型
7.2 全局函数 + 友元方案
标准做法是把 operator<< 定义为全局函数,再声明为 Date 的友元以访问私有成员:
cpp
// Date.h
class Date {
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
// ...
private:
int _year, _month, _day;
};
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
cpp
// Date.cpp
ostream& operator<<(ostream& out, const Date& d) {
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out; // 返回 ostream&,支持连续输出:cout << d1 << d2;
}
istream& operator>>(istream& in, Date& d) {
while (true) {
cout << "请依次输入年月日:>";
in >> d._year >> d._month >> d._day;
if (!d.CheckDate()) {
cout << "日期非法:";
d.Print();
cout << "请重新输入!!!" << endl;
} else {
break;
}
}
return in; // 返回 istream&,支持连续输入
}
使用起来就很自然了:
cpp
Date d1, d2;
cin >> d1 >> d2; // 连续输入
cout << d1 << d2; // 连续输出
cout << d1 - d2 << endl; // 输出两个日期的天数差
八、完整日期类的实现
整合以上所有知识,实现一个功能完备的日期类。
Date 类设计总览:
Date
- int _year
- int _month
- int _day
- Date(int year, int month, int day)
- bool CheckDate() : const
- void Print() : const
- int GetMonthDay(int year, int month) : const
- bool operator<(const Date& d) : const
- bool operator==(const Date& d) : const
- bool operator<=(const Date& d) : const
- bool operator>(const Date& d) : const
- bool operator>=(const Date& d) : const
- bool operator!=(const Date& d) : const
- Date& operator+=(int day)
- Date operator+(int day) : const
- Date& operator-=(int day)
- Date operator-(int day) : const
- int operator-(const Date& d) : const
- Date& operator++()
- Date operator++(int)
- Date& operator--()
- Date operator--(int)
- friend operator<<(ostream& out, const Date& d)
- friend operator>>(istream& in, Date& d)
Date.h
cpp
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date {
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
bool CheckDate() const;
Date(int year = 1900, int month = 1, int day = 1);
void Print() const;
int GetMonthDay(int year, int month) const {
assert(month > 0 && month < 13);
static int monthDayArray[13] =
{ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && ((year % 4 == 0 && year % 100 != 0)
|| (year % 400 == 0)))
return 29;
return monthDayArray[month];
}
// 比较运算符
bool operator<(const Date& d) const;
bool operator<=(const Date& d) const;
bool operator>(const Date& d) const;
bool operator>=(const Date& d) const;
bool operator==(const Date& d) const;
bool operator!=(const Date& d) const;
// 加减天数
Date operator+(int day) const;
Date& operator+=(int day);
Date operator-(int day) const;
Date& operator-=(int day);
// 自增/自减
Date operator++(int); // 后置++
Date& operator++(); // 前置++
Date operator--(int); // 后置--
Date& operator--(); // 前置--
// 日期差
int operator-(const Date& d) const;
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
Date.cpp(核心实现)
cpp
#include"Date.h"
bool Date::CheckDate() const {
if (_month < 1 || _month > 12
|| _day < 1 || _day > GetMonthDay(_year, _month))
return false;
return true;
}
Date::Date(int year, int month, int day)
: _year(year), _month(month), _day(day) {
if (!CheckDate()) {
cout << "非法日期:";
Print();
}
}
void Date::Print() const {
cout << _year << "/" << _month << "/" << _day << endl;
}
// -------- 比较运算符 --------
bool Date::operator<(const Date& d) const {
if (_year < d._year) return true;
if (_year == d._year && _month < d._month) return true;
if (_year == d._year && _month == d._month && _day < d._day) return true;
return false;
}
bool Date::operator==(const Date& d) const {
return _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator<=(const Date& d) const { return *this < d || *this == d; }
bool Date::operator>(const Date& d) const { return !(*this <= d); }
bool Date::operator>=(const Date& d) const { return !(*this < d); }
bool Date::operator!=(const Date& d) const { return !(*this == d); }
// -------- += --------
Date& Date::operator+=(int day) {
if (day < 0) return *this -= (-day); // 处理负天数
_day += day;
while (_day > GetMonthDay(_year, _month)) {
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13) {
_year++;
_month = 1;
}
}
return *this;
}
// -------- + (复用 +=) --------
Date Date::operator+(int day) const {
Date tmp = *this;
tmp += day;
return tmp;
}
// -------- -= --------
Date& Date::operator-=(int day) {
if (day < 0) return *this += (-day); // 处理负天数
_day -= day;
while (_day <= 0) {
--_month;
if (_month == 0) {
_month = 12;
--_year;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
// -------- - (复用 -=) --------
Date Date::operator-(int day) const {
Date tmp = *this;
tmp -= day;
return tmp;
}
// -------- 自增自减 --------
Date& Date::operator++() { // 前置 ++
return *this += 1;
}
Date Date::operator++(int) { // 后置 ++(多一个 int 参数只是占位标记)
Date tmp = *this;
*this += 1;
return tmp; // 返回旧值
}
Date& Date::operator--() { // 前置 --
return *this -= 1;
}
Date Date::operator--(int) { // 后置 --
Date tmp = *this;
*this -= 1;
return tmp;
}
// -------- 两个日期的差 --------
int Date::operator-(const Date& d) const {
int flag = 1;
Date max = *this;
Date min = d;
if (*this < d) {
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max) {
++min;
++n;
}
return n * flag;
}
// -------- 流运算符 --------
ostream& operator<<(ostream& out, const Date& d) {
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
istream& operator>>(istream& in, Date& d) {
while (true) {
cout << "请依次输入年月日:>";
in >> d._year >> d._month >> d._day;
if (!d.CheckDate()) {
cout << "日期非法:";
d.Print();
cout << "请重新输入!!!" << endl;
} else {
break;
}
}
return in;
}
测试用例
cpp
int main() {
// 测试 1:加减天数
Date d1(2024, 7, 12);
Date d2 = d1 + 100;
d1.Print(); // 2024/7/12
d2.Print(); // 2024/10/20
d1 += 30000;
d1.Print(); // 大约 2106 年
// 测试 2:自增自减
Date d3(2024, 7, 13);
Date ret1 = d3++; // 后置:先返回旧值,再加
ret1.Print(); // 2024/7/13
d3.Print(); // 2024/7/14
Date d4(2024, 7, 13);
Date ret2 = ++d4; // 前置:先加再返回
ret2.Print(); // 2024/7/14
d4.Print(); // 2024/7/14
// 测试 3:负天数(+= 和 -= 互相调用处理)
Date d5(2024, 7, 12);
d5 += -100; // 等价于 d5 -= 100
d5.Print();
// 测试 4:两个日期相差天数
Date d6(2034, 10, 1);
Date d7(2024, 6, 30);
cout << d6 - d7 << endl; // 约 3745 天
// 测试 5:流输入输出
Date d8, d9;
cin >> d8 >> d9;
cout << d8 << d9;
cout << "相差" << d8 - d9 << "天" << endl;
return 0;
}
设计思想总结
+复用+=:让+调用+=,避免代码重复- 带符号处理 :
+=遇到负数时转为-=,-=遇到负数时转为+= - const 的正确使用 :不修改
*this的函数加const(如+、<、==等) - 流运算符必须用全局函数 + 友元
- 日期合法性校验:构造函数和流输入都做了日期校验
总结
下篇知识体系总览:
C++类和对象 下篇
初始化列表
位置: 构造函数冒号后
必须用的场景
const 成员
引用成员
无默认构造的类类型成员
执行顺序 = 声明顺序
C++11 成员变量默认值
explicit
禁止隐式类型转换
只能修饰单参构造
避免意料之外的转换
static 成员
static 成员变量
类外定义(全局数据区)
所有对象共享
不计入 sizeof
static 成员函数
没有 this 指针
只能访问 static 成员
友元 Friend
友元函数
友元类
关系是单向的、不可传递
内部类
嵌套在类内的类
默认是外部类的友元
大小独立
匿名对象
无名字的临时对象
生命周期仅当前行
流运算符重载
operator<<
operator>>
必须全局函数 + 友元
完整 Date 类
全部运算符重载
构造、拷贝、流
自增自减
日期差值
三篇合集 ------ C++类和对象知识脉络总图:
下篇:高级特性
中篇:进阶核心
上篇:基础入门
类的定义与封装
访问限定符 public/private
实例化与内存模型
this 指针机制
构造函数
析构函数与 RAII
运算符重载入门
拷贝构造函数
浅拷贝 vs 深拷贝 ⭐
赋值运算符重载
比较/算术运算符重载
const 成员函数
初始化列表
explicit 关键字
友元与内部类
static 成员
匿名对象
流插入/流提取重载
完整 Date 类实战
本篇覆盖了类和对象中最进阶的内容:
- 初始化列表:const 成员、引用成员、无默认构造的类成员必须在这里初始化
- explicit:禁止隐式类型转换,防止意外的临时对象
- static 成员:属于整个类而非单个对象,存在全局数据区
- 友元:允许外部函数/类访问私有成员,破坏封装但有时必要(如流重载)
- 内部类:嵌套定义在类内部的类,默认是外部类的友元
- 匿名对象 :临时对象,常用于简化代码(如
Solution().Func()) - 流运算符重载 :
operator<<和operator>>必须用全局函数 + 友元 - 完整日期类:综合以上所有知识的实战案例
三篇回顾
| 篇目 | 核心内容 |
|---|---|
| 上 | 类定义、访问限定、实例化、this 指针、构造/析构、运算符重载入门 |
| 中 | 拷贝构造、深/浅拷贝、赋值重载、运算符重载深入、const 成员函数 |
| 下 | 初始化列表、explicit、static、友元、内部类、匿名对象、流重载、完整日期类 |
三篇涵盖了 C++ 类和对象的核心知识体系,从入门到进阶再到高级应用,构成了完整的学习路径。建议在理解本章内容后,多动手实现几个带有运算符重载的类(如复数类 Complex、分数类 Fraction、字符串类 String),以加深理解。