C++ 构造函数;赋值函数(=);析构函数

时间:2021-08-21 01:34:09

一.构造函数

类通过一个或几个成员函数来控制其对象的初始化过程,这些函数成为构造函数。构造函数的任务是初始化类的数据成员,只要类的对象被创建,就会执行构造函数。构造函数与类同名且没有返回值。

①默认构造函数

默认构造函数是没有参数的构造函数,当类没有定义任何构造函数时,编译器会帮我们合成一个默认构造函数,如:

class A

private:

int x ;

double *p;

B y;
}; 


当我们写下:

A a;

编译正常通过,因为有合成的默认构造函数,但是合成的默认构造函数不能对内置类型以及指针、数组进行初始化,所以上面 x,p是未定义的,而y只有在有默认构造函数时才能被A的合成默认构造函数初始化,否则不能通过编译


当类有定义任意构造函数时,不再会有默认的构造函数,除非我们在定义一个

class A

public:

A(int n): x(n) { }

private:

int x ;

double *p;

B y;
}; 


A a;//不能通过编译

需改成:

class A

public:

A() { }

A(int n): x(n) { }

private:

int x ;

double *p;

B y;
}; 

②拷贝构造函数

拷贝构造函数只有一个参数同时是本类型的一个引用变量,如:

A(const A &b) { }

在我们未定义拷贝构造函数时,编译器也会帮我们产生一个默认的拷贝构造函数,进行位拷贝,这在有指针的情况下是不适用的,如:

A a1;

A a2(a1);


这时a2.p == a1.p; 这显然不是我们所希望的,所以需要我们自己定义从而为p分配内存


当我们不希望拷贝现象存在,可将拷贝构造函数定义为私有的

③构造函数的初始化表
      构造函数有个特殊的初始化方式叫“初始化表达式表”(简称初始化表)。初始化表位于函数参数表之后,却在函数体{}之前。这说明该表里的初始化工作发生在函数体内的任何代码被执行之前。
      以下是构造函数初始化表的使用规则和注意点:
      1)如果类存在继承关系,派生类必须在其初始化表里调用基类的构造函数。

      2)类的const 常量只能在初始化表里被初始化,因为它不能在函数体内用赋值的方式来初始化。
      3)成员对象初始化的次序完全不受它们在初始化表中次序的影响,只由成员对象在类中声明的次序决定。这是因为类的声明是唯一的,而类的构造函数可以有多个,因此会有多个不同次序的初始化表。如果成员对象按照初始化表的次序进行构造,这将导致析构函数无法得到唯一的逆序。


2. 赋值函数(=)

赋值函数为运算符重载,写法如下

A &operator=(const A &b);//赋值函数

赋值函数与拷贝构造函数一样,在未定义时编译器也会默认生成,也是位拷贝的方式;


拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用:

A a1;

A a2(a1);//拷贝构造函数

A a3 = a1;//拷贝构造函数

a3 = a2;//赋值函数


3.析构函数

析构函数主要任务是销毁对象的成员变量,成员变量按照初始化的逆序销毁,函数形式如下:

~A ( ) { }

编译器也会帮我们合成默认析构函数,同样对有指针成员的类不适用


4.构造函数和析构函数可以是虚函数吗

构造函数不能是虚函数,因为虚函数需要 vtable ,而vtable只有在只有在构造函数调用后才生成,这就产生矛盾了。

析构函数可以是虚函数,当类有派生类时,析构函数最好是虚函数,这样才能正确释放资源。