1. 再谈构造函数
1.1 构造函数体赋值
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
构造函数里面是赋值
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
1.2 初始化列表
----红标1 是类对象整体的的定义
----红标2 是类里成员的声明,之后就不用带类型了,可以给缺省值,它是给初始化列表用的
红标3 是类里成员的定义,初始化的地方
----初始化列表是所有成员定义的地方,不写的话,内置类型给随机值,自定义类型就调用他的构造函数
----成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
1.3 explicit关键字
1.3.1 单参数构造函数的隐式类型转换
构造函数不仅可以构造与初始化对象,对于接收单个参数的构造函数,还具有类型转换的作用。接收单个参数的构造函数具体表现:
- 构造函数只有一个参数
- 构造函数有多个参数,除第一个参数没有默认值外,其余参数都有默认值
- 全缺省构造函数
有了单参数的隐式类型转换就可以完成以下操作
1.3.2 多参数的隐式类型转换
// C++11 支持多参数的隐式类型转换普通的构造函数B bb1(1, 1);多参数的隐式类型转换B bb2 = { 2, 2 };const B& ref2 = { 3,3 };等于号+尖括号
1.3.3 匿名对象
主要用于一次调用,完成类里面的工作
类名+括号(参数)
// 有名对象 特点:生命周期在当前局部域A aa6(6);// 匿名对象。特点:生命周期只在这一行A(7);SeqList s;A aa7(7);s.PushBack(aa7);s.PushBack(A(8));class Solution {
public:int Sum_Solution(int n) {// ...return n;}
private:
};Solution sl;sl.Sum_Solution(10);Solution().Sum_Solution(100);Date d1(2023, 7, 28);cout << d1;cout << Date(2023, 7, 28);
1.3.4 explicit
用于阻止类的隐式类型转换
class A
{
public:explicit A(int i)//A(int i):_a(i){cout << "A(int i)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
A aa1 = 2; 报错
2. Static成员
2.1 定义
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
2.2 特点
突破类域的三种方式:
用于访问静态成员函数,静态成员变量(公有),成员函数!!
1---- A::Print()
2---- A aa1; aa1.Print()
3---- A* ptr = nullptr; ptr->Print()
其中成员函数:第一种方法不可以
class A
{
public:A(){cout << "A()" << endl;++n;++m;}A(const A& t){++n;++m;}~A(){--m;}// 静态成员函数的特点:没有this指针static int GetM(){return m;}static void Print(){// x++; // 不能访问非静态,因为没有thiscout << m <<" " << n << endl;}
private:// 静态成员变量属于所有A对象,属于整个类// 这里是声明// 累积创建了多少个对象 static int n;// 正在使用的还有多少个对象static int m;int x = 0;
};
// static定义在类外
int A::n=0;
int A::m=0;
3. 友元
3.1 友元函数
现在尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。
3.2 友元类(朋友卡全家桶)
Tips:
1 友元函数可访问类的私有和保护成员,但不是类的成员函数
2 友元函数不能用const修饰,因为const修饰的是this指针
3 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
4 一个函数可以是多个类的友元函数
5 友元函数的调用与普通函数的调用原理相同
4. 内部类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
4.1 特性
1、B类受A类域和访问限定符的限制,其实他们是两个独立的类2、内部类默认就是外部类的友元类,反之不可以
class A
{
//public:class B{public:void FuncB(){A aa;B是A的友元,可以直接访问A的私有属性aa._a = 1;}private:int _b;};A的作用域标识符是公有还是私有都可以这样定义B类对象void func(){B bb;}private:int _a;
};int main()
{cout << sizeof(A) << endl;A aa;这是只有A对B的作用域标识符是公有的时候才可以:A::B bb1;return 0;
}
4.2 1+2+3-----+n
5. const 和 引用 以及编译器的优化
只要是函数内部不改变类对象,并且还是传引用调用,推荐形参都加上const
const 引用会延长匿名对象的生命周期
编译器的优化:一个步骤里面,连续构造再拷贝构造,会省略拷贝构造
连续的拷贝构造也会被合并成一个
再更抽象的玩法:返回的就是一个临时对象,临时对象的值直接被ret1拿来拷贝构造。本质上下面的三种情况一样(本来是 return 2 语句先----构造一个匿名对象,然后----拷贝构造给返回的临时对象,返回的临时对象再----拷贝构造给ret1,整合这个步骤中的构造,拷贝构造,拷贝构造成为一个构造。)