默认构造有三种
- 无参构造
- 有参全缺省构造 (没有缺省值,就没有默认值=没有默认构造)
- 拷贝构造
不写任何构造,系统会默认生成三个。只要自己写其中一种构造,其往上的构造函数,系统都不再自动生成。
比如写了有参,则系统不再生成无参,但仍生成拷贝。写了拷贝,则无参及有参都不再生成。
以往我们初始化成员都是用函数内初始化来完成,现在试试初始化列表的方式初始化。
Date(int year, int month, int day) : _year(year), _month(month) , _day(day) {}
自定义成员 推荐用初始化列表初始化
初始化列表可以理解为成员变量定义的位置,类中包含以下成员,必须放在初始化列表位置进行初始化:
- 引用成员变量
- const成员变量
- 自定义类型成员(且该类没有默认构造函数时)
class A //自定义类型 { public: A(int a) :_a(a) {} private: int _a; }; class B { public: B(int a, int ref) :_aobj(a) ,_ref(ref) ,_n(10){} private: A _aobj; // 没有默认构造函数 int& _ref; // 引用 const int _n; // const };
//1.2初始化列表
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();// A.输出1 1// B.程序崩溃// C.编译不通过// D.输出1 随机值
}
答案是 D,
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。因为a1声明在a2后面,所以先执行a2初始化而不是a1.
老版本下,d2的构造方式是,先用2022构造一个类似d1的对象,再通过拷贝构造将临时的d1传给d2。而有的优化过的编译器则会直接构造。
验证,结果是并不会输出第二行的cout 。因为没有调用拷贝构造:Date(const Date&d)
看下面的三个函数
调用 func2 会报错的原因:
- func1能过:是因为在隐式转化(优化)下,编译器对"你好"构造给了临时string对象temp,然后再把临时对象temp拷贝构造给了形参s,优化后就是直接构造。
- 但是,func2没能隐式转化,由临时常量"你好",直接给string & s 调用,这是权限放大,故报错。
- func3:对string& s加了const修饰,是权限平移,所以又能过了。
上述问题得出规律,传参尽量用引用,引用尽量用const
构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。
面试题
实现一个类,计算程序中创建出了多少个类对象。