1.为什么要用初始化列表
首先了解初始化列表,就要了解下什么是函数体内初始化
代码
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
上面的就是函数体内初始化,也是我们平常用的初始化
那么我们来了解下初始化列表
代码
class Date
{
public:
Date(int year = 1, int month = 1,int day = 1)
:_year(year)
, _month(month),
_day(day)
{
}
private:
int _year;
int _month;
int _day;
};
这就是初始化列表,初始化列表可以和函数体内初始化一起·用,比如列表初始化了year,month,你用函数体内初始化day是没问题的
我们了解了这二种初始化后,我们就来了解下什么时候用初始化列表
因为有些成员只能在定义初始化
图片
可以看到在这种情况下,用函数体内初始化,就不行了会报错
如果是初始化列表就不会出现这种问题了
图片
还有一个用初始化列表的地方,类调用自定义类型,这时候有二种办法,让编译器通过,一,创建一个构建函数,二,用初始化列表
代码
#include<iostream>
using namespace std;
int num = 10;
class A
{
public:
A(int a = 0)
:_a(a)
{
}
private:
int _a;
};
class Date
{
public:
Date(int year, int n, int a)
:_n(n)
, _num(num)
,_aa(a)
{
_year = year;
}
private:
//定义时初始化
//也可以不初始化,后面在赋值
int _year;
//只能在定义初始化
const int _n;
int& _num;
A _aa;
};
用初始化列表就可以显示初始化了,
int main()
{
Date d1(2020, 9, 26);
return 0;
}
总结:
- 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
- 类中包含以下成员,必须放在初始化列表位置进行初始化:
(1)引用成员变量
(2)const成员变量
(3)自定义类型成员(该类没有默认构造函数)
其他的成员可以在初始化列表,也可以在函数体内初始化
建议:尽量用初始列表初始化
出个问题 下面代码会打印什么
class A
{
public:
A(int a)
:_a1(a)
, _a2(_a1)
{}
void Print() {
cout << _a1 << " " << _a2 << endl;
}
private:
int _a2;
int _a1;
};
int main() {
A aa(1);
aa.Print();
}
这道题应该很多人的第一反应是打印1 1,毕竟1传过去给a,a初始化a1,a1初始化给a2
但是这道题的答案是1 随机值
因为初始化顺序是是按声明的顺序来初始化的,所以一开始1传给a ,a赋给a2,然后a1没赋值随机值
运行代码
2.explicit关键字的隐式类型转换
代码
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year)
:_year(year)
{
cout << "year " << endl;
}
Date(const Date& d)
{
cout << "const Date&d " << endl;
}
private:
int _year;
};
int main()
{
Date d1(2022);
Date d2 = 2022;
}
打印结果
第二个写法是隐式类型转换的转换,把2022转换成Date类型赋值给d2
别看这里二个好像调用都是拷贝构造,但其实第二个不是,比较老一点的编译器是构造+拷贝,现在比较新的编译器都是合二为一
而我为什么讲隐式类型转换呢?c++有个关键字explicit
加上explicit的运行结果
可以看到报错,不能转换类型
3.静态成员变量
1.概念
全局变量是不好的因为在一些库里面可能会出现名字冲突,而这时就出现了静态成员变量static
静态成员变量写法
#include<iostream>
using namespace std;
class A
{
public:
A()
{
++_count1;
}
A(const A& aa)
{
++_count2;
}
//private:
// 静态成员变量属于整个类,属于类的所有对象
static int _count1;
static int _count2; // 声明
};
// 定义
int A::_count1 = 0;
int A::_count2 = 0;
A Func(A a)
{
A copy(a);
return copy;
}
int main()
{
A a1;
A a2 = Func(a1);
// 静态成员变量属于整个类,属于类的所有对象
cout << a1._count1 << endl;
cout << a2._count1 << endl;
cout << a1._count2 << endl;
cout << a2._count2 << endl;
cout << A::_count1 << endl;
cout << A::_count2 << endl;
}
因为静态成员变量属于整个类,属于类的所有对象,所以要有定义,上面的那种写法是属于公有写的,如果是私有上面代码是不通过的
不过通过这种静态成员变量,避免使用了全局变量,这就是静态成员变量的优势之一
那么怎么调用私有的静态成员变量
在类里面创建一个函数
代码
#include<iostream>
using namespace std;
class A
{
public:
A()
{
++_count1;
}
A(const A& aa)
{
++_count2;
}
// 成员函数也可以是静态,static成员函数没有this指针
static int GetCount1()
{
return _count1;
}
static int GetCount2()
{
return _count2;
}
private:
// 静态成员变量属于整个类,所以类的所有对象
static int _count1;
static int _count2; // 声明
};
// 定义
int A::_count1 = 0;
int A::_count2 = 0;
A Func(A a)
{
A copy(a);
return copy;
}
int main()
{
A a1;
A a2 = Func(a1);
cout << a1.GetCount1() << endl;
cout << a2.GetCount2() << endl;
cout << A::GetCount1() << endl;
cout << A::GetCount2() << endl;
}
不过用静态成员变量要注意的是,static成员函数没有this指针
证明图片
可以看到static成员函数不能访问成员变量,因为里面没有this指针,所以就报错了
2.特性
- 静态成员为所有类对象所共享,不属于某个具体的实例
- 静态成员变量必须在类外定义,定义时不添加static关键字
- 类静态成员即可用类名::静态成员或者对象.静态成员来访问
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
- 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值
4.C++11 的成员初始化新玩法
给缺省值,这里初始化是在初始化列表
变成这样
但是静态成员变量是不能给缺省值的
图片
接下来我们就用静态成员变量做道题
题目链接
描述
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
数据范围: 0 < n \le 2000<n≤200
进阶: 空间复杂度 O(1)O(1) ,时间复杂度 O(n)O(n)
题目解析:
这题可以用构造函数来做,没创建一个变量都会去调用一次构造函数
class num
{
public:
num()
{
_ret+=_i;
_i++;
}
static int GetRet()
{
return _ret;
}
private:
static int _ret;
static int _i;
};
int num::_ret=0;
int num::_i=1;
class Solution {
public:
int Sum_Solution(int n) {
num a[n];
return a[0].GetRet();
}
};