构造函数相关知识

二木 王者

构造函数相关知识

关于普通构造函数的两种写法

一种是我们常见的普通写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<bits/stdc++.h> 
using namespace std;
class text
{
public:
text(int num1, int num2)
{
this->a = num1;
this->b = num2;
}
int geta(){return a;}
private :
int a;
int b ;

};

int main()
{
text arr(10,30);
cout << arr.geta() << endl;

return 0;
}

另一种就是初始化列表的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<bits/stdc++.h> //从算法哥们那里学来的万能开头,平常使用很方便,但是加载的库很多,写项目不能使用
using namespace std;

class text
{
public:
text(int num1, int num2): a(num1),b(num2) //直接进行初始
{
}
int geta(){return a;}

private :
int a;
int b ;
};
int main()
{
text arr(10,30);
cout << arr.geta() << endl;
return 0;
}

使用构造函数初始化列表的好处是可以在对象创建时直接初始化成员变量,而不是在构造函数体内部进行赋值操作。这样可以提高效率,并且在某些情况下,还可以避免一些潜在的问题,比如 const 成员变量或引用类型成员变量的初始化。

复制构造函数(拷贝构造函数)

在我们写代码的时候,会遇到这样一个需求,我们需要将我们的这个现有的类,复制给另外一个新创建的一个类。c++中会提供默认的拷贝构造函数,但是默认构造函数实现的拷贝为浅拷贝,也就是说,如果在类中如果有指针操作,在调用两个对应的析构函数的时候,就会造成对同一空间释放两次的操作,会出现问题,因此就需要我们自己在类中写一个拷贝构造函数来拷贝我们类中的数据。进行显示的复制

下面是一个简单的演示,关于怎么调用与怎么写:

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
27
28
29
30
31
#include<bits/stdc++.h>
using namespace std;
class text
{
public:
text(){ //无参构造函数

}
text(int num1, int num2): a(num1),b(num2){ //初始化列表式的构造函数

}
int geta(){return a;}

text(text& T) //复制构造函数
{
this->a =T.a;
this->b =T.b;
}
private :
int a;
int b ;
};
int main()
{
text arr(10,30);
cout << arr.geta() << endl;
text arr2(arr); //调用arr2的拷贝构造函数对于arr中的数据进行拷贝
cout << arr2.geta() << endl;

return 0;
}

移动构造函数

在我们显式的调用复制构造函数我们可以发现,我们调用复制构造函数,使用的是一个一个的进行复制,也就是说在中途,会产生一个空间的周转,从而产生浪费,试想一下,如果我要复制的数据很多,比如一万个,甚至更多,这样就会对空间产生巨大的浪费,因此我们需要使用移动构造函数来帮我们解决这个问题。

对于移动构造,我们对常常听到移动语义的概念。所谓移动语义,指的就是以移动而非深拷贝的方式初始化含有指针成员的类对象。简单的理解,移动语义指的就是将其他对象(通常是临时对象)拥有的内存资源“移为己用”。实现将资源的所有权从一个对象转移到另一个对象,而不进行资源的复制。移动语义允许将右值(临时对象、将被销毁的对象)的资源转移到新的对象中,而不进行资源的复制。这对于大型对象或拥有独占资源的对象来说,可以显著提高性能。

移动构造函数和移动赋值运算符通常使用右值引用(R-value reference)来实现。通过使用 && 符号来声明右值引用,可以明确指明一个对象是一个右值,从而触发移动语义。

下面是一个移动构造函数的实现

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <vector>
class MyVector
{
public:
// 默认构造函数
MyVector() : data(nullptr), size(0) {}

// 移动构造函数
MyVector(MyVector &&other) : data(other.data), size(other.size) //
{
other.data = nullptr; // 将源对象的指针置为空,避免重复释放资源
other.size = 0;
}

// 析构函数
~MyVector()
{
delete[] data;
}

// 使用链表,实现元素的添加,动态存储
void addElement(int element)
{
int *newData = new int[size + 1];
for (int i = 0; i < size; i++)
{
newData[i] = data[i];
}
newData[size] = element;
delete[] data;
data = newData;
size++;
}

// 打印元素
void printElements()
{
for (int i = 0; i < size; i++)
{
std::cout << data[i] << " ";
}
std::cout << std::endl;
}

private:
int *data; //链表头
int size; //实时数组大小
};

int main()
{
// 创建一个 MyVector 对象并添加元素
MyVector vec1;
vec1.addElement(1);
vec1.addElement(2);
vec1.addElement(3);

// 使用移动构造函数创建一个新的 MyVector 对象,vec1 被释放, vec1 不再包含元素
MyVector vec2(std::move(vec1));

// 打印 vec2 的元素
vec2.printElements();
return 0;
}

这里通过使用move函数将左值强制转化为右值.这里我们可以看到, 如果我们使用普通的构造函数, 在使用深拷贝的时候,需要将我们内存中的所有数据都要一次复制到一个新的空间中去,如果我们建立的这个数组非常大的话,这对于内存来说是一个极大的浪费,因此使用移动构造,使用转移所有权的方式,将我们的一个类下的数据,转移到另一个类之下,这样就避免的内存空间巨大的无谓使用, 实现了提高代码的效率和性能。

C++构造函数的三种写法 - 知乎 (zhihu.com)

C++高阶知识:深入分析移动构造函数及其原理 | 音视跳动科技 (avdancedu.com)

 评论