构造函数

1、引入

在c++的类中,构造函数是一种特殊的成员函数,在每次创建创建一个类的时候会默认调用构造函数进行初始化工作。

构造函数用来完成一些必要的初始化工作,有了构造函数之后,就无需再单独写初始化函数,并且也不必担心忘记调用初始化函数。

2、基本概念

构造函数具有如下几个特点

  • 名字与类名相同,可以有参数,但是不能有返回值(void也不行)
  • 作用是对对象进行初始化工作,如给成员变量赋值等。
  • 如果定义类时没有写构造函数,系统会生成一个默认的无参构造函数,默认构造函数没有参数,不做任何工作。
  • 如果定义了构造函数,系统不再生成默认的无参构造函数
  • 对象生成时构造函数自动调用,对象一旦生成,不能在其上再次执行构造函数
  • 一个类可以有多个构造函数,为重载关系

3、默认无惨构造函数

下面来看一个简单的例子。

1
2
3
4
5
6
7
8
9
class Complex {//复数类
private:
double real, imag;//分别表示实部以及虚部
public:
void set(double r, double i) {
real = r;
imag = i;
}
};

对于上面的复数类,real表示实部,imag表示虚部,set(set(double r, double i))函数为初始化函数,我们并没有为其编写构造函数,此时编译系统将生成一个默认的无参构造函数。

假设有如下调用:

1
2
3
4
5
int main() {
Complex c1; //默认构造函数被调用
Complex* c2 = new Complex; //默认构造函数被调用
return 0;
}

在对象生成的时候,编译系统自动调用默认的无参构造函数对其进行初始化工作(什么都没做),此时我们必须自己调用set(double r, double i)函数才能对其进行初始化操作。

4、编写构造函数

接下来我们为上面的类编写一个构造函数

1
2
3
4
5
6
7
8
9
class Complex {//复数类
private:
double real, imag;//分别表示实部以及虚部
public:
Complex(double r, double i = 0) {
real = r;
imag = i;
}
};
1
2
3
4
5
int main() {
Complex c1;//错误,参数不匹配
Complex* c2 = new Complex;//错误,参数不匹配
return 0;
}

当我们在调用上面的代码生成对象c1、c2时,编译系统会给我们报错。原因是我们编写的构造函数需要接收至少一个参数,而我们在初始化的时候没有给出任何的参数。

对上面的代码进行如下的改动:

1
2
3
4
5
int main() {
Complex c1(1);//OK
Complex* c2 = new Complex(2,5);//OK
return 0;
}

此时c1、c2就可以正常地生成。

  • 对于c1对象,用1来初始化实部,用缺省的0初始化虚部。
  • 对于c2对象,用2来初始化实部,用5初始化虚部。

5、构造函数重载

对于同一个类,可以有多个构造函数,只要参数个数或类型不同就可,他们之间为重载的关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Complex {//复数类
private:
double real, imag;//分别表示实部以及虚部
public:
//构造函数
Complex();
Complex(double r);
Complex(double r, double i);
Complex(Complex c1, Complex c2);
};
Complex::Complex() {
real = 0;
imag = 0;
}
Complex::Complex(double r) {
real = r;
imag = 0;
}
Complex::Complex(double r, double i) {
real = r;
imag = i;
}
Complex::Complex(Complex c1, Complex c2) {
real = c1.real + c2.real;
imag = c1.imag + c2.imag;
}

在上面的类中,分别写了四个构造函数

  • Complex()无参构造函数,real和imag都初始化为0
  • Complex(double r)将real初始化为r,imag初始化为0
  • Complex(double r, double i)将real初始化为r,imag初始化为i
  • Complex(Complex c1, Complex c2)将real初始化为c1.real + c2.real,imag初始化为c1.imag + c2.imag

对于上面的类,假设我们有如下调用:

1
2
3
4
5
6
7
int main() {
Complex c1;//调用Complex()构造函数
Complex c2(2);//调用Complex(double r)构造函数
Complex c3(2, 3);//调用Complex(double r, double i)构造函数
Complex c4(c1, c2);//Complex(Complex c1, Complex c2)构造函数
return 0;
}
  • 对于c1对象,调用Complex()无参构造函数,将real和imag都初始化为0
  • 对于c2对象,调用Complex(double r)构造函数,将real初始化为2,imag初始化为0
  • 对于c3对象,调用Complex(double r, double i)构造函数,将real初始化为2,imag初始化为3
  • 对于c4对象,调用Complex(Complex c1, Complex c2)将real初始化为c1.real + c2.real=2,imag初始化为c1.imag + c2.imag=0

6、构造函数在数组中的使用

下面我们通过一个实例来查看对与对象数组是如何调用构造函数进行初始化的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Complex {//复数类
private:
double real, imag;//分别表示实部以及虚部
public:
//构造函数
Complex() {
cout << "无参构造函数初始化" << endl;
}
Complex(double r) {
cout << "一个参数的构造函数初始化" << endl;
}
Complex(double r, double i) {
cout << "两个参数的构造函数初始化" << endl;
}
};

我们为上面的类写了三个构造函数,在调用无参构造函数时输出无参构造函数初始化,调用一个参数的构造函数时输出一个参数的构造函数初始化,调用两个构造函数初始化时输出两个参数的构造函数初始化。

假设我们有如下的对象生成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main() {
cout << "array1" << endl;
Complex array1[3];

cout << "array2" << endl;
Complex array2[3] = { 1,2 };

cout << "array3" << endl;
Complex array3[3] = { 1,Complex(1,2) };

cout << "array4" << endl;
Complex* array4 = new Complex[3];

cout << "array5" << endl;
Complex* array5[3] = { new Complex(1),new Complex(2,3) };

delete[]array4;
return 0;
}
  • 对于array1生成的三个对象,我们没有为其指定参数,所以都调用无参构造函数进行初始化。
  • 对于array2生成的三个对象,我们指定了前两个的参数表为1和2,所以调用一个参数的构造函数进行初始化,第三个对象没有指定参数,所以调用无参构造函数进行初始。
  • 对于array3生成的三个对象,第一个对象参数为1,所以调用无参构造函数初始化,第二个对象参数为(1,2),所以调用两个参数的构造函数初始化,对于第三个对象,我们没有指定参数,所以调用无参构造函数初始化。
  • array4为一个Complex类的指针,通过new运算符动态分配三个对象,并将其首地址返回给array4,在此我们并没有为new出来的三个对象指定初始化参数,所以三个对象都调用无参构造函数初始化。
  • array5为一个Complex类的指针数组,包含三个指针对象。第一个new Complex(1)生成了一个对象,且参数为1,所以调用一个参数的构造函数;第二个元素通过new Complex(2,3)生成一个对象,且参数为(2,3),所以调用两个参数的构造函数;第三个我们没有为其动态分配内存空间,所以不会导致对象的生成,仅存在一个对象指针。所以array5仅生成了两个对象。

对于上面的程序,我们可以得到如下的运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
array1
无参构造函数初始化
无参构造函数初始化
无参构造函数初始化
array2
一个参数的构造函数初始化
一个参数的构造函数初始化
无参构造函数初始化
array3
一个参数的构造函数初始化
两个参数的构造函数初始化
无参构造函数初始化
array4
无参构造函数初始化
无参构造函数初始化
无参构造函数初始化
array5
一个参数的构造函数初始化
两个参数的构造函数初始化