1. 前言
关于类和对象的大致内容已经结束
本篇文章主要是介绍一些冗杂的细节
虽然本节的内容属于对类和对象锦上添花
但在很多特定的场所下,还是特别使用的!
本章重点:
本篇文章重点讲解初始化列表
友元,匿名对象和类中的static成员
并且介绍类中的内部类的概念
话不多说,进入正题!
2. 初始化列表
构造函数中,为一个成员赋值
不能叫做对此成员初始化
只能说对此成员赋初始值
由此引出初始化列表:
真正初始化成员变量的地方!
初始化列表:
以冒号开始接着以逗号分隔的成员列表
每个"成员变量"后面跟一个
放在括号中的初始值或表达式
例如:
class Date { public: Date(int year, int month, int day) : _year(year) , _month(month) , _day(day) {} private: int _year; int _month; int _day; };
初始化列表不止可以像上面一样用
还可以在函数体中再次对变量操作:
Date(int year, int month, int day) : _year(year) , _month(month) , _day(day) { _year++; _day--; } //或者这样 Date(int year, int month, int day) : _year(year) , _month(month) { _month = month; }
2.1初始化列表的作用
有些变量在初始化时必须对它赋值
比如:
- const成员变量
- 引用成员变量
- 没有默认构造的自定义类型成员
然而在构造函数函数体中的赋值
不叫对变量初始化,用上面的类型会报错所以此时必须用初始化列表
class B { public: B(int a, int ref) :_aobj(a) ,_ref(ref) ,_n(10) {} private: A _aobj; // 没有默认构造函数的自定义类型 int& _ref; // 引用成员 const int _n; // const修饰成员 };
在之后的学习中,尽量使用初始化列表
进行初始化,因为对于自定义成员来说
不管有没有显示写初始化列表
它都会优先使用初始化列表初始化
2.2 初始化列表再理解
初始化列表中,初始化变量的顺序
是变量在类中声明的顺序
比如以下代码:
class A { public: A(int a) :_a1(a) ,_a2(_a1) {} private: int _a2; int _a1; }; int main() { A aa(1); }
此时,_a2会先初始化,_a1再初始化
_a2初始化时_a1还是随机值
所以_a2就被初始化成了随机值
而_a1会被初始化为1!
对于C++11缺省值的理解:
成员变量声明时给的缺省值
实际上就是给初始化列表的!
当用户没有显示传参初始化时
编译器会用用户定义的缺省值
当用户显示传参后,缺省值失效
使用用户传的值初始化!
class A { public: A(int a)//没有显示传参就用缺省值初始化 :_a1(a) ,_a2(a) {} private: int _a2 = 1; int _a1 = 2; }; }
3. static成员
概念:
- 声明为static的类成员称为类的静态成员
- 用static修饰的成员变量
称之为静态成员变量 - 用static修饰的成员函数
称之为静态成员函数 - 静态成员变量一定要在类外进行初始化
static成员的特性:
- 静态成员为所有类对象共享,放在静态区
- 静态成员变量必须在类外定义
类中只是声明 - 类静态成员即可用
类名::静态成员
或对象.静态成员
访问 - 静态成员函数没有隐藏的this指针
不能访问任何非静态成员 - 静态成员受访问限定符的限制
使用举例:
class B { public: static int Add(int x,int y);//没有this指针,无法访问类中成员 static int a;//在类中声明 }; int B::a = 10;//在类外定义
此类的所有成员共同享有这个静态变量
4. 友元的概念
假设一个函数我想定义在类外
但是我又想访问类中的私有成员
只能将私有成员改为共有再访问
这种操作就破坏了类的封装!
引入友元解决此问题:
友元函数可以直接访问类的私有成员
它是定义在类外部的函数,不属于任何类
但需要在类的内部声明
声明时需要加friend关键字
举例说明:
class Date { friend int Add(int x,int y);//友元函数的声明 public: Date(int year = 1900, int month = 1, int day = 1) : _year(year) , _month(month) , _day(day) {} private: int _year; int _month; int _day; }; int Add(int x,int y)//友元函数的定义 { x+=_year; y+=_month-_day; return x+y; }
对友元函数的说明:
- 友元函数可访问类的私有和保护成员
但不是类的成员函数 - 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明
不受类访问限定符限制 - 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数相同
4.1 友元类
除了有友元函数可以访问类私有成员外
声明友元类也可以达到一样的效果
内部类就是友元类的典型代表!
注:sizeof(外部类)的大小和内部类无关
class A { private: static int k; int h; public: class B // B天生就是A的友元 { public: void foo(const A& a) { cout << k << endl;//OK cout << a.h << endl;//OK } }; };
5. 类的匿名对象
先定义一个类:
class Date { public: Date(int year = 1900, int month = 1, int day = 1) : _year(year) , _month(month) , _day(day) {} private: int _year; int _month; int _day; };
使用匿名对象:
Date d = Date(2023,8,1);
这里的:Date(2023,8,1)就是匿名对象
特点:
- 生命周期只有一行
- 没有名字,在初始化或销毁时
自动调用构造或析构函数
匿名对象使用场景:
当定义第一个变量只是为了调用
类中的一个函数,并且调用完后
此变量不会再被使用,此时可用匿名对象
匿名对象极大的简化了代码行!
6. 总结以及拓展
类和对象的所有内容已经讲解完毕
若友遗漏或不对的地方,请在评论指出!
拓展:explicit关键字
构造函数不仅能构造和初始化对象
对于单个参数或除第一个参数无默认值
其余均有默认值的构造函数
还有隐式类型转换的作用
比如:
class Date { public: Date(int year) : _year(year) {} private: int _year; int _month = 1; int _day = 1; }; int main() { Date d = 1999; return 0; }
此处的Date d=1999就是隐式类型转换
然而explicit关键字可以阻止
构造对象时使用隐式类型转换
简直是一个非常卑鄙的关键字[doge]
使用方法:加在构造函数前
explicit Date(int year) : _year(year) {}
🔎 下期预告:C++内存管理 🔍