构造函数是成员函数的一种

  • 名字与类名相同,可以有参数,不能有返回值,void也不行。
  • 作用的对对象进行初始化,给成员变量赋初值
  • 如果定义是没写构造函数,则编译器默认生成一个无参的构造函数(默认生成的构造函数无参数,不做任何操作
  • 对象生成时,构造函数自动被调用,对象一旦生成,就再也不能在其上执行构造函数
  • 一个类可以有多个构造函数

构造函数执行必要的初始化工作,有了构造函数就不必在专门写初始化函数,也不用调用

复制构造函数

只有一个参数,即对同类对象的引用

形如X::X(X&)或者X::X(const x &) 二者选一  后者能以常量对象作为参数

如果没有定义辅助构造函数,那么编译器生成默认构造函数,默认的复制构造函数完成复制功能


对比:

无参构造函数(构造函数)它不一定存在,只有你不写构造函数时,编译器才会帮你生成构造函数,而你写了时编译器不会生成   

而复制构造函数一定存在,你不写编译器就帮你写


class Complex
{
private:
	double real, imag;
};
Complex c1;//调用缺省无参的构造函数
Complex c2(c1);//调用缺省的复制构造函数,将c2初始化和c1一样

 对于复制构造函数来说,不允许出现X::X(X)的构造函数,因为复制构造函数的参数必须是引用,不能是对象

复制构造函数起作用的情况

  1. 当用一个对象初始化同类的另一个对象时;

Complex c1; Complex c2(c1);  等价于 Complex c1=c2;(这不是赋值语句,这是初始化语句)


  1. 一个函数的参数如果是对象的话,就一定调用的是复制构造函数,这是C++的规定
void fun(A a1){};
int main()
{
  A a2;
  fun(a2);
  return 0;
}


  1. 如果一个函数的返回值是类A的对象时,则函数返回时,A的复制构造函数被调用

注意:对象间的赋值并不导致复制构造函数被调用

Complex c1; Complex c2(c1);  等价于 Complex c1=c2;
但是第二种不会导致复制函数被调用,第一种会


常量引用参数的使用

void func(Myclass obj){cout<<"fun"<<endl;}

  • 这样的函数调用时开销比较大,因为会引发复制函数的调用
  • 可以考虑使用Myclass&类型作为参数
  • 如果希望确保实参的值在函数中不应被改变,也可以加上const关键字


类型转换构造函数

定义

  • 定义转换构造函数的目的是实现类型的自动转换
  • 只有一个参数,而且不是复制构造函数的构造函数,一般就可以看作是转换构造函数(特殊的构造函数)
  • 当需要的时候,编译系统就会自动调用转换构造函数,建立一个无名的临时对象
//类型转换构造函数
class Complex
{
public:
	double real,imag;
	Complex(int r) { cout << "  type"; real = r; imag = 0; };
	Complex(double r, double i) { real = r; imag = i; };
};

int main()
{
	Complex c1(2, 5);
	Complex c2 = 12;
	c1 = 9;//把9自动转换成为一个临时的Complex对象
	cout << c1.real << "," << c1.imag;
	return 0;
}

析构函数

  • 名字与类名相同,在前面加‘~',没有参数和返回值,一个类最多只有一个析构函数
  • 析构函数在对象消亡时即自动被调用,可以定义析构函数来在对象消亡前后做善后工作,比如释放分配的空间
  • 如果定义时,没写析构函数,可以默认编译器自动生成一个缺省的析构函数,可以默认为缺省的析构函数什么也不做
  • 如果定义了析构函数,则编译器不在生成

对象数组生命周期结束时,对象数组的每个元素的析构函数都会被调用

class A
{
public:
	~A()
	{
		cout << "end";
	}
};
int main()
{
	A a[2];
	cout << "start";
	return 0;
}         //输出结果stat end end

delete的运算导致析构函数的调用,如果不用delete,那么这个对象不会凋亡,也不会引发析构函数的调用


Ctest* test;
test = new Ctest; //构造函数调用
delete test;//析构函数调用
--------------------------------
Ctest* test;
test = new Ctest[3]; //构造函数调用3次
delete []test;//析构函数调用3次

析构函数在对象作为函数的返回值返回后也会被调用

构造函数和析构函数容易误解的地方

  • 构造函数,构造函数只负责初始化,空间已经有了,(构造函数不造房子,只是负责装修)
  • 析构函数,并不负责回收整个对象所占的存储空间,只是在对象的存储空间被回收之前做一些善后工作(不负责房子的拆迁,只是负责一些善后工作,在被拆掉之前,搬走一些东西)

复制构造函数在不同编译器的表现也不一样