1.再谈构造函数
在之前的类和对象(中)我们了解到了构造函数,在创建对象的时候编译器通过调用构造函数,给对象中的各个成员变量赋一个合适的初始值,但是:
class A { public: A(int a) :_a(a) {} private: int _a; }; class B { public: B(int a, int ref) {} private: A _aobj; int& _ref; const int _n; };
运行这段代码发现,这种写法是有问题的,会报错
这是因为在调用构造函数给成员变量赋值的过程本质不是变量的初始化,而是变量的赋值,而变量的初始化是在初始化列表进行的。
1.1初始化列表
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
以Date类为例:
那么,使用初始化列表需要注意什么?/ 初始化列表的特点是什么?
- 每个成员变量在初始化列表中只能出现一次;
- 类中包含以下成员的时候:
- 引用成员变量,
- const修饰的成员变量,
- 自定义类型成员(且该类没有默认构造函数时);
- 尽量使用初始化列表初始化;
- 成员变量在初始化列表中的初始化顺序是成员变量在类中的声明次序,与初始化列表中的次序无关;
特点讲解
特点一:初始化列表的本质是定义初始化成员变量,初始化只能有一次。
特点二:引用和const修饰的变量在定义的时候必须初始化,并且后面不能再修改;实例化对象的时候,会自动调用默认构造函数,如果没有默认构造函数的话就需要传参,这里括号内的就是参数。
特点三:不管那种类型的成员变量,都是需要走一遍初始化列表的,出于效率的考虑,我们建议使用初始化列表初始化。
特点四:下面这段代码的运行结果是1 随机值。
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.2explicit关键字
explicit的作用:修饰构造函数,禁止(隐式)类型转换。
我们还是以日期类为例:
class Date { public: Date(int year) :_year(year) ,_month(1) ,_day(1) {} Date& operator=(const Date& d) { if (this != &d) { _year = d._year; _month = d._month; _day = d._day; } return *this; } void Print() { cout << _year << " " << _month << " " << _day << endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2022); d1.Print(); d1 = 2023; d1.Print(); return 0; }
这段代码是可以正常运行的,原理是在d1.Print() = 2023
这句代码中,使用常量2023
创建了一个类型为Date的临时变量,然后将这个临时变量拷贝给d1。但是如果在构造函数前面加上explicit就禁止了这个类型转换,所以代码将不能运行。
2.static成员
2.1概念
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。
在这里分享一个面试题:实现一个类,计算程序中创建出了多少个类对象
读到这个题目,我们首先先到的应该就是使用一个全局变量或者静态变量,每次创建新的变量的时候使该变量自增。但是使用全局变量的风险是很大的,因为全局变量在任意位置都可以修改,所以我们考虑把这个静态变量放在类里面。
class A { public: A() { N++; } A(const A& t) { N++; } static int GetN() { return N; } private: static int N; }; int A::N = 0; void TestA() { cout << A::GetN() << endl; A a1, a2; A a3(a1); cout << A::GetN() << endl; }
2.2特性
- 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
- 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
- 类静态成员即可用类名::静态成员或者对象.静态成员来访问
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
- 静态成员也是类的成员,受public、protected、private 访问限定符的限制