1. 再谈构造函数
1.1 构造函数体赋值 vs 初始化列表
在创建对象时,编译器通过调用构造函数给成员变量赋初值。 虽然在构造函数体内赋值可以让对象有初始值,但这被称为赋初值而非初始化。因为初始化只能进行一次,而函数体内可以多次赋值。
初始化列表:以冒号开始,接着是以逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或表达式。
cpp
class Date
{
public: // 推荐:使用初始化列表
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day) {}
private:
int _year;
int _month;
int _day;
};
必须使用初始化列表的情况:
引用成员变量
const 成员变量
自定义类型成员(且该类没有默认构造函数时)
重要细节: 成员变量在类中声明的次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
- 建议: 尽量使用初始化列表。对于自定义类型成员,无论是否写在列表中,编译器总会先尝试在列表中初始化它。
1.3 explicit 关键字
构造函数不仅用于初始化,对于单个参数 (或多参数但第一个参数外都有默认值)的构造函数,还具有隐式类型转换的作用。
- 隐式转换示例: `Date d1 = 2023;` 编译器会用 2023 构造一个临时对象,再赋值给 d1。
- explicit 的作用: 在构造函数前加上 `explicit` 关键字,可以禁止这种隐式类型转换,提高代码的可读性和安全性。
2. Static 成员 (Static Members)
2.1 概念与定义
声明为 `static` 的类成员称为类的静态成员。
- 存储: 静态成员为所有类对象所共享,不属于某个具体对象,存放在静态区。
- 定义: 静态成员变量必须在类外定义(初始化),类中只是声明。类外定义时不加 `static` 关键字。
- 访问: 可以通过
类名::静态成员或对象.静态成员访问。
2.2 Static 成员函数
静态成员函数没有隐藏的 this 指针。
- 限制: 不能访问任何非静态成员(因为没有 `this` 指针)。
- 权限: 依然受 public/protected/private 访问限定符的限制。
cpp
class A {
public:
A() { ++_scount; } // 每创建一个对象,计数+1
~A() { --_scount; }
static int GetACount() { return _scount; } // 静态函数获取静态变量
private:
static int _scount; // 类内声明
};
int A::_scount = 0; // 类外定义及初始化
3. 友元 (Friends)
友元提供了一种突破封装的方式,允许外部函数或类访问某个类的私有(private)和保护(protected)成员。虽然便利,但会增加耦合度,破坏封装,不宜滥用。
3.1 友元函数 (Friend Function)
友元函数是定义在类外部的普通函数,不属于任何类,但在类内部使用 `friend` 关键字声明。
- 典型应用: 重载 `operator<<` 和 `operator>>`。
- 痛点: 如果将 `<<` 重载为成员函数,`this` 指针会占据第一个参数位置,导致调用变成 `d1 << cout`,非常别扭。因此必须重载为全局函数。
- 实现: 为了让全局函数能访问类的私有成员(如 `_year`),需要将其声明为友元。
cpp
class Date {
// 声明友元函数,它可以访问 Date 的私有成员
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int y, int m, int d) : _year(y), _month(m), _day(d) {}
private:
int _year;
int _month;
int _day;
};
// 类外定义,无需 friend 关键字,也无需 Date:: 限定符
ostream& operator<<(ostream& _cout, const Date& d) {
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
3.2 友元类 (Friend Class)
如果类 B 是类 A 的友元类,则类 B 的所有成员函数都可以访问类 A 的私有成员。
- 单向性: B 是 A 的友元,不代表 A 是 B 的友元。
- 无传递性: C 是 B 的友元,B 是 A 的友元,不能推断出 C 是 A 的友元。
- 不能继承: 友元关系不能被继承。
4. 内部类 (Inner Class)
4.1 概念与特性
如果一个类定义在另一个类的内部,这个类就叫做内部类。
- 独立性: 内部类是一个独立的类,不属于外部类。
sizeof(外部类)不包含内部类的大小。 - 天生友元: 内部类天生就是外部类的友元。内部类可以直接访问外部类的 static 成员、枚举等,也可以通过外部类对象访问其私有成员。但反过来不行(外部类不是内部类的友元)。
- 访问权限: 内部类可以定义在外部类的 public、protected、private 区域。
cpp
class A {
private:
static int k;
int h;
public:
class B { // B 是 A 的内部类,也是 A 的友元
public:
void foo(const A& a) {
cout << k << endl; // OK: 直接访问外部类 static
cout << a.h << endl; // OK: 访问外部类私有成员
}
};
};
int A::k = 1;
int main() {
A::B b; // 定义内部类对象
b.foo(A());
return 0;
}