1.什么是构造函数?
一种特殊的成员函数,创建一个对象时,系统需要调用对象的构造函数进行初始化,类似于OC的init方法
2.构造函数的特点
- 函数名与类名一样,书写格式:类名(参数列表)
- 没有返回值,加上void也不行
- 每个类都有一个默认的公共的无参构造函数,但只要额外增加了一个构造函数,系统将不提供默认的空构造函数
- 构造函数可以重载,也就是说,可以提供多个构造函数
3.构造函数的其他知识点
如果对象是一个全局变量,那么它的构造函数调用会先于main函数
4.什么是转换构造函数
- 带有一个参数的构造函数
- 可以将其他类型的数值转为对象
例如:Person p(20);//前提是需要提供下面的构造函数
直接调用转换构造函数:Person(int age)
如果是一下代码需要分情况讨论
Person p;
p = 30;
如果Person类重载了=(int)运算,第二句代码会直接用重载过的运算符进行运算
如果没有重载,则::先隐式调用转换构造函数:Person(int age)产生一个临时的Person对象,再将临时对象赋值给p{实现是调用operator=(const Person& other)函数
执行语句:Person temp(30);p.operator = (temp);
}
-------这种情况下会调用两次构造函数,第一句调用空构造函数,第二句调用转换构造函数,这种调用称为隐式调用
5.如何阻止转换构造函数的隐式调用?
只需要在声明只有一个参数的构造函数上加上explicit关键字(明显的)例如:
explicit Person(int age);
P.S.--C++创建对象数组和释放对象数组的一些问题
Person persons[2] = {20,30}; //会调用两次转换构造函数
Person *persons = new Person[2];//persons指向数组的第一个元素
Person *persons = new Person[2]{20,30};跟上面一条语句一样,多了调用转换构造函数的过程
第一种创建方法创建出来的对象数组在栈里面,不需要我们手动管理,第二三种方发创建的对象在堆里,需要手动释放
而且释放对象数组的时候,应该用指向首元素的指针去释放,跟释放对象不一样,还要加上方括号,例如:
delete [] persons;
6.拷贝构造函数
---拷贝构造函数的基本信息
什么是拷贝构造函数 :::只有一个参数,且参数为该类对象引用的构造函数 可以使用一个已存在的对象来初始化一个同类型的新对象 每一个类都有一个默认的公共的拷贝构造函数
拷贝构造函数的格式::: 类名(const 类名& 参数名) { }
加上const后,就不能在函数内部直接修改参数的成员变量
-----------拷贝构造函数的调用以及为什么参数是引用
Person p; Person p2(p);// 相当于 Person p2 = p;
上面的第2行代码就会调用Person的拷贝构造函数
因此,用一个对象初始化另外一个对象时,系统会自动调用拷贝构造函数
也说明了拷贝构造函数的形参不能是对象,只能是对象引用 因为调用拷贝构造函数时,需要传入一个实参对象来初始化形参对象,这时候又会调用拷贝构造函数,造成死循环
------------拷贝构造函数一般在什么时候被调用
1...明显地用一个对象初始化另外一个对象 Person p; Person p2(p); // 相当于 Person p2 = p;
2...当函数的形参是对象:调用函数时,会创建一个局部的形参对象,并且用实参对象来初始化形参对象,这里会调用拷贝构造函数 void setBook(Book book);
3...当函数的返回值是对象:系统会利用返回的对象再创建初始化一个新的临时对象,这时也调用了拷贝构造函数 Book getBook() { return _book; // _book是一个成员变量 }
综上所述:一般都使用 对象引用 来取代 对象 作为函数参数或返回值
C++初始化列表
Person::Person(int age, double height) : _age(age), _height(height) { }
后面的: _age(age), _height(height)就是构造函数的初始化列表
上面代码的最终效果等价于下面的代码 Person::Person(int age, double height) { _age = age; _height = height; }
----------------------但是他们的区别是什么?
构造函数的执行分为2个阶段:
- 初始化阶段(对象分配内存时就进行的初始化赋值操作)
- 普通计算阶段(对象初始化完毕后才进行的操作)
下面代码中成员变量的赋值是在“初始化阶段”完成的,给对象分配内存时,成员变量默认就有值了
Person::Person(int age, double height) : _age(age), _height(height)
{
}
下面代码中成员变量的赋值是在“普通计算阶段”完成的,给对象分配好内存后,还需要执行2行代码给成员变量赋值
Person::Person(int age, double height)
{
_age = age;
_height = height;
}
总上所述:推荐使用构造函数的初始化列表来初始化成员变量
---------还有就是如果Person类的成员变量有Book类,并且Book类只提供了一个参数的构造方法(覆盖了系统提供的空构造函数)
此时如果创建Person对象会报错,因为初始化Person之前或初始化其成员变量Book,但是Book没有空构造函数,则会报错
此时就需要用初始化列表来初始化Book
Person() : _book(15.0){};这样Book在分配内存的时候直接调用转换构造函数创建book对象
-----------------初始化列表还常用与const和引用的初始化,因为如果类Person里面有