前言
最近在复习C++的基础知识,在复习的时候才发现,对C++构造函数这方面的内容一直没有很清晰的了解,所以在此稍作记录。
一、构造函数
类的构造函数是类的一种特殊的成员函数,其任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
二、构造函数分类
在C++当中,构造函数主要可以分为一下四类:
1.默认构造函数
Test(); //无参
2.初始化构造函数
Test(int x,int y); //带参数,可初始化类内数据成员
3.复制(拷贝)构造函数
Test(const Test*temp); //通过另一个类成员初始化该成员
其中若非自己单独声明,编译器都会给我们隐形构造这几类函数。
设有个Test类内有如下数据成员,之后的样例都基于此:
class Test{
private:
int x;
int y;
int* p;
}
1.默认构造函数
因为此种构造函数无参,我们可以在其之后的内部进行赋值,当初始化语句如下时,将默认使用此构造函数:
//构造函数
Test() {
x = 0;
y = 0;
p = new int(10);
*p = 0;
}
//初始化方式
Test test1;
2.初始化构造函数
在这种构造函数当中,我们可以采用两种方式进行对类内成员的赋值,一种是列表初始化,而另一种是内部赋值,样例如下:
//构造函数(二者选一,不能共存)
Test(int a,int b) {
x = 0;
y = 0;
}
Test(int a, int b) :x(a), y(b) {}
//初始化方式
Test test1(10,20);
值得注意的是,C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。也就是说采用初始化列表的话,构造函数本体实际上不需要有任何操作,因此效率更高。
同时凭借C++的重载特性,根据不同形参也可有多个构造函数。
3.复制(拷贝)构造函数
该构造函数主要是使用一个已有的对象去初始化另一个对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中。样例如下:
//构造函数
Test(const Test& t)
{
x = t.x;
y = t.y;
p = t.p;
}
//初始化方式
Test t1;
Test t2(t1);
但是运行此样例后我们会发现运行出错,其主要原因是浅拷贝,深拷贝问题。
由于成员数据中含有指针,当我们简单的将指针等于另一个对象的指针后,当程序最后执行析构函数时,将对指针进行回收,但是由于两个指针都指向同一地址,而同一地址的两次析构,就会产生错误,所以正确样例如下:
Test(const Test& t)
{
x = t.x;
y = t.y;
p = new int(10);
*p=*(t.p);
}
//初始化方式
Test t1;
Test t2(t1);
在第二个样例中我们可以发现,对指针重新开辟了一块地址,而p=(t.p)这条语句成功将第二个对象指针所指内容赋值给第一个对象。
所以在这时,是有两个对象,两个指针指向不同地址,但其内容相同。
再次从输出直观体现这一差别:
一般在类成员数据中含有指针都要考虑深浅拷贝的问题。
特殊"构造"函数
在查找相关资料的时候,发现很多博文在构造函数中加入了一类为:赋值构造函数,主要声明方式如下:
//函数声明
Test& operator=(const Test& t)
{
if (this == &t)
return *this;
delete p;
x = t.x;
y = t.y;
p = new int;
*p = *(t.p);
}
//初始化方式
Test t1(10,20);
Test t2;
t1=t2;
但自己总结后发现,这种操作只能称为赋值函数,而不是构造函数,因为当我们进行接下来的操作时:
Test t2=t1;
进行单步执行时发现,该语句只会执行上文的拷贝构造函数,而不是赋值函数。
但在构造函数的定义中,其是在类实例声明时即会调用,所以其并不能称为构造函数。
同时值得注意的是,在赋值函数中我们需要第一步首先判断是否是同一对象,防止循环引用,重复赋值等问题。
总结
在本文中,主要对C++中几类构造函数进行了介绍。
如文中出现纰漏,可在评论区和我交流。
同时指针操作需要记得释放内存,在此说明。