什么是构造函数
构造函数是在创建类对象时,由系统自动调用,初始化新对象的函数,给其中的成员变量赋值。构造函数没有返回值,名字与类名相同,有参数,所以可以进行函数重载,构造函数大致可以分为一下几类:
- 无参构造:没有参数的构造函数,也是默认构造函数
- 有参构造:含有参数的构造函数
- 拷贝构造:用一个已有对象来初始化一个新对象
- 移动构造:把已有对象的资源给新对象,旧对象不再占有资源
拷贝构造又可以分为深拷贝和浅拷贝。
一般来说,我们定义一个类,系统通常会提供三个函数,即默认构造、拷贝构造和赋值运算符重载,当我们实现了其中的一个构造函数后,系统将不再提供默认构造函数。
无参构造
当我们没有自己定义任何构造函数时,系统会提供给这个类一个默认构造函数,但是这个构造函数内部实际上什么都没做,我们一般可以用午餐构造来初始化一个默认的对象,比如说我们定义了一个时间类,我们可以在无参构造中将它的各个属性(时,分,秒)赋值为0,避免我们直接使用时输出的随机值,不符合实际要求。
有参构造
当我们只实现了有参构造而没有实现无参构造时,系统将不再提供默认的无参构造,有参构造,通常用来将传入的参数赋值给对应的属性(注意是赋值,不是初始化,初始化参数列表是给属性初始化),我们在创建对象的时候,直接在对象名后面用括号给出对应参数的值即可。
class m_time {
public:
int h, m, s;
m_time(int h, int m, int s) {
this->h = h;
this->m = m;
this->s = s;
}
};
int main() {
//m_time a;//报错,没有默认构造
m_time a(1,2,3);
return 0;
}
拷贝构造
拷贝构造是用一个已经存在的对象初始化一个新对象,二者属性中的值相等,拷贝构造函数的参数必须是引用,这是因为我们在调用拷贝构造时,需要给参数对象传参,这时如果参数不是引用,则需要先调用拷贝构造给参数初始化,但这个函数本身就是拷贝构造函数,所以会导致无限递归下去,最终栈空间溢出,爆栈。
拷贝构造又可以细分为深拷贝和浅拷贝,这个区别主要体现在属性中有在堆区空间申请内存的类,深拷贝是重新申请一块同样大小的空间,并且将其中的值复制到新对象的堆区空间,而,浅拷贝只做简单的值复制,导致两个指针指向同一块堆区空间,这样会有两个问题,第一是,在对其中一个对象进行操作时,会影响到另一个对象的属性;第二是在最终释放时,程序会因为重复释放同一块空间导致崩溃。
class A {
public:
int num;
int* p = nullptr;
A(int num) {
this->num = num;
p = new int[3];
}
/*A(const A& other) {
this->num = other.num;
this->p = other.p;
}*/
//上面是浅拷贝,对指针也只是简单的赋值
A(const A& other) {
this->num = other.num;
this->p = new int[3];
for (int i = 0; i < 3; i++) {
this->p[i] = other.p[i];
}
}
//深拷贝,单独再开辟一块空间,将其中的值也复制过去
~A() { if (p) delete[] p; }
//析构函数,清理资源,防止内存泄漏
};
int main() {
A a(2);
A b(a);
cout << b.num << endl;
//输出 2
return 0;
}
移动构造
移动构造是一个新的概念,与右值引用相关(右值引用的概念可以看http://t.csdnimg.cn/mjQOp),主要是可以用来捕获将亡值的资源(一般是函数返回值),将其分配给新对象,避免接收返回值时,再次调用拷贝构造,消耗系统资源,通俗来讲,移动构造的意思就是:“我的东西不要了,给你”。
#include<iostream>
using namespace std;
class A {
public:
int num;
int* p = nullptr;
A(int num) {
this->num = num;
p = new int[3];
}
//左值引用,拷贝构造
A(A& other) {
this->num = other.num;
this->p = new int[3];
for (int i = 0; i < 3; i++) {
this->p[i] = other.p[i];
}
cout << "拷贝构造" << endl;
}
//右值引用,移动构造
A(A&& other) {
this->num = other.num;
this->p = other.p;
other.p = nullptr;
cout << "移动构造" << endl;
}
~A() { if (p) delete[] p; }
//析构函数,清理资源,防止内存泄漏
};
A test01() {
A a(2);
return a;
}
int main() {
A b(test01());
cout << b.num << endl;
return 0;
}
输出结果为:
关于构造函数的其他内容,比如初始化参数列表还有属性中有其他类对象的构造的过程,在之后的文章还会继续分享给大家。
标签:分析,int,构造,num,全面,拷贝,other,构造函数 From: https://blog.csdn.net/weixin_58234579/article/details/140428781