类和对象(下)
大家好,这篇我们继续学习类和对象。
1. 成员初始化列表
在构造函数中,我们常常需要初始化类的成员变量。C++ 提供了两种方式:
在构造函数体中赋值
使用成员初始化列表
成员初始化列表的语法是在构造函数参数列表后加:,然后列出要初始化的成员。
class Date { public: Date(int year, int month, int day, int hour) : _year(year) , _month(month) , _day(day) , _t(hour) // 初始化对象成员 { // 构造函数体 } private: int _year; int _month; int _day; Time _t; // 对象成员 };为什么推荐使用初始化列表?
效率更高:直接初始化,而不是先默认构造再赋值。
必须使用的情况:
const成员引用成员
没有默认构造函数的类类型成员
class Date { private: int& _ref; // 引用成员,必须在初始化列表中绑定 const int _n; // const 成员,必须在初始化列表中初始化 Time _t; // 如果 Time 没有默认构造函数,也必须显式初始化 };2. 成员初始化顺序
重要提醒:成员的初始化顺序与初始化列表的书写顺序无关,只与类中成员的声明顺序有关。
class A { public: A(int a1, int a2) : _a2(a2) 1,再初始化 _a2 , _a1(_a2) // 注意!这里 _a2 还未初始化,结果是未定义行为 {} private: int _a1 = 2; int _a2 = 2; };正确做法:按照声明顺序书写初始化列表,避免依赖未初始化的成员。
3. explicit 构造函数
默认情况下,单参数构造函数会发生隐式转换,有时这会带来意外行为。
class A { public: A(int a1) : _a1(a1) {} // 如果不加 explicit,下面的转换会自动发生 private: int _a1; }; A aa = 10; // 隐式转换为 A(10)加上explicit可以禁止这种隐式转换:
explicit A(int a1) : _a1(a1) {} A aa = 10; // 编译错误,必须显式写 A aa(10);C++11 还支持多参数构造函数使用{}初始化列表,但 explicit 同样可以阻止隐式转换。
4. 静态成员
静态成员属于类本身,而不是对象。特点:
用
static关键字声明必须在类外定义并初始化
所有对象共享同一份
class A { public: static int GetCount() { return _scount; } private: static int _scount; }; int A::_scount = 0; // 类外定义 int main() { cout << A::GetCount() << endl; // 即使没有对象也能访问 return 0; }用静态成员实现全局计数器,或者实现 1+2+...+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; int Sum_Solution(int n) { Sum arr[n]; // 创建 n 个对象,每个对象构造时累加 return Sum::GetRet(); }5. 友元(friend)
友元破坏了封装,但提供了灵活性。友元可以访问类的 private/protected 成员。
友元函数
友元类
class B; // 前向声明 class A { friend void func(const A& a, const B& b); // 友元函数 friend class B; // 友元类 private: int _a = 1; }; class B { public: void visit(const A& a) { cout << a._a << endl; // B 是 A 的友元,可以访问 } };6. 内嵌类(嵌套类)
类可以定义在另一个类内部,称为内嵌类。
class A { public: class B { // 内嵌类 public: void foo(const A& a) { cout << a._h << endl; // 可以访问外部类的 private 成员 } }; private: int _h = 1; static int _k; };内嵌类默认是外部类的友元,可以直接访问外部类的所有成员(包括 static)。
7. 拷贝控制
当类管理资源时,需要自定义:
拷贝构造函数
拷贝赋值运算符
析构函数
C++11 还增加了移动构造函数和移动赋值运算符。
class A { public: A(const A& aa) { /* 深拷贝 */ } A& operator=(const A& aa) { if (this != &aa) { /* 深拷贝 */ } return *this; } ~A() { /* 释放资源 */ } };另外,临时对象的构造和析构要特别注意。现代编译器会进行拷贝省略,但在某些情况下仍会产生多次构造/析构。
总结
以上就是 C++ 类和对象的高级特性。
优先使用初始化列表
注意成员初始化顺序
谨慎使用友元
需要管理资源时,记得实现拷贝控制
希望这篇文章对你有帮助!
