在之前的博客中,我们大致了解了类和对象的内容和其的定义方式,接下来我们对其进一步做讨论 -- 构造函数。
目录
1.构造函数
构造函数是类中一种特殊的成员函数,类中对象的创建和初始化工作有它来完成,它存在以下特点:
- 被声明为共有(public);
- 函数名和类名相同;
- 可以形成函数重载;
- 无函数返回类型;
- 不能被显示调用。
对于构造函数,系统默认的构造函数是无参数的构造函数,当一个类中没有定义任何构造函数的情况下,编译器便会为其生成默认的构造函数(无参数,无函数体)。
当然我们也可以自己定义类的构造函数,也可以定义和系统默认生成构造函数相同的构造函数,也可以生成我们自己的构造函数。
class Point {
private:
int x;
int y;
public:
Point() {
}
void set(int xvalue, int yvalue) {
x = xvalue;
y = yvalue;
}
void show() {
cout << "(" << x << "," << y << ")" << endl;
}
};
对于类Point,其中Point(){},便是构造函数,也是系统默认会生成的构造函数。 注意构造函数定义为public类型。
class Point {
private:
int x;
int y;
public:
Point() {
}
Point(int xvalue, int yvalue) {
x = xvalue;
y = yvalue;
}
void set(int xvalue, int yvalue) {
x = xvalue;
y = yvalue;
}
void show() {
cout << "(" << x << "," << y << ")" << endl;
}
};
像这样,我们便实现了Point构造函数的重载。
在我们实现类的相关构造方法之后,我们便可以创建类的对象的同时,来对类中成员数据进行初始化。
#include<iostream>
using namespace std;
class Point {
private:
int x;
int y;
public:
Point() {
}
Point(int xvalue, int yvalue) {
x = xvalue;
y = yvalue;
}
void set(int xvalue, int yvalue) {
x = xvalue;
y = yvalue;
}
void show() {
cout << "(" << x << "," << y << ")" << endl;
}
};
int main() {
Point a(1, 1);
//a.set(1, 1);
a.show();
return 0;
}
我们便不必再定义类中成员数据的初始化方法--set,直接在创建类的对象时,便可完成初始化。不过需要注意的是,当我们需要对已经创建的对象中的成员数据(非public)内容进行修改时,仍需要借助public的成员方法。(构造函数只是提供的只是初始化成员数据)
当我们定义了带参的构造函数,系统便不会生成默认的构造函数了,于是当我们在后面定义类的对象时,要注意参数的传入。或者我们形成重载,给出具体的无参且空函数体的构造函数。
最后我们讲述一个问题,那便是当我们不实现构造函数的情况下,编译器默认生成的构造函数看起来似乎并没有什么必要。因为就算我们调用了默认的初始化构造函数,创建Poing类的对象中的成员数据依旧是随机值。
实际上,在C++中,将数据类型分为内置类型(基本类型:int,double……)和自定义类型(class、struct和union等),系统默认生成的构造函数会对自定义类型的成员调用其默认成员函数。
从上图中我们可以看出,编译器生成的默认构造函数对自定义类型的成员_t调用了它的默认成员函数。
2.成员初始化表
除了在构造函数中给出成员数据的初始化方式之外,C++还提供了成员初始化表这样一种方式来对成员进行初始化。我们一般用于初始化类中引用成员的初始化,因为引用类型的数据必须在定义的过程中,即刻进行初始化,但是在类的成员数据定义中,我们是不能对其进行赋值初始化的。
所以当我们在类中定义引用类型的数据时,需要借助成员初始化表来实现对引用成员的初始化。因为构造函数的特殊性,一般在类的定义过程中,就会调用类的构造函数,这边相当于完成引用数据定义中即刻初始化的要求。
#include<iostream>
using namespace std;
class Sample {
private:
int x;
const int y;
int& xr;
public:
Sample(int x1,int y1) :x(x1), y(y1), xr(x) {
}
void show() {
cout << "x=" << x << "y=" << y << endl;
}
};
int main() {
Sample s(1,2);
s.show();
return 0;
}
如上述代码展示,对于const关键字修饰的常量也可以通过初始化列表的方式,来对其完成初始化。
3.具有默认参数的构造函数
当我们构造函数中的参数值除特殊情况外,一般不对其做出改变时,我们便可将其定义为带默认参数的构造函数。
如上图展示,当我们在定义Date对象时,如果不对其进行传值初始化,则会用默认的参数值为对象中的数据成员赋值,而当我们对对象中的成员函数赋值时,数据成员会从左到右依次被赋值。
值得注意的是,当我们使用具有默认参数的构造函数时,如果此时在类中同时定义无参且无函数体的构造函数时,就会报错,如下所示:
错误原因也十分的简单,即是对重载函数的调用不明显。