构造函数主要有以下几种
-
默认构造函数
不带任何参数,可以自己显式定义,也可以由C++隐式定义
-
一般构造函数
也称重载构造函数,可以有各种参数形式,一个类可以有多个一般构造函数
-
转换构造函数
只有一个参数,根据参数类型的对象创建一个该类对象
-
拷贝构造函数
用一个已存在的类对象A去构造一个不存在的类对象B,会调用拷贝构造函数,参数是一个左值对象;在对象被拷贝后临时对象和拷贝构造的对象各自占有不同的堆内存,就是一个副本。从下图中可以看到,临时对象和新建对象a申请的堆内存同时存在。
-
移动构造函数
参数是一个右值对象,让临时对象控制的内存的空间转移给构造出来的对象,这样就相当于把它移动过去了。从下图中可以看到,原本由临时对象申请的堆内存,由新建对象a接管,临时对象不再指向该堆内存。
-
拷贝赋值函数
用一个已存在的类对象A赋值给一个已存在的类对象B,会调用拷贝赋值函数
-
移动赋值函数
类似于移动构造函数,新对象直接接管原内存,避免了内存的一次拷贝
class Sample_C {
public:
//默认构造函数
Sample_C() {
m_k = 0;
m_f = NULL;
}
//一般构造函数
Sample_C(int p_k, float* p_f) {
m_k = p_k;
m_f = p_f;
}
//转换构造函数
Sample_C(int p_k) {
m_k = p_k;
m_f = NULL;
}
//拷贝构造函数
Sample_C(const Sample_C& p_sample) {
m_k = p_sample.m_k;
m_f = new float[m_k];
memcpy(m_f, p_sample.m_f, m_k*sizeof(float));
}
//移动构造函数
Sample_C(const Sample_C&& p_sample) {
m_k = p_sample.m_k;
m_f = p_sample.m_f;
}
//拷贝赋值函数
Sample_C& operator=(const Sample_C& p_sample) {
m_k = p_sample.m_k;
m_f = new float[m_k];
memcpy(m_f, p_sample.m_f, m_k*sizeof(float));
}
//移动赋值函数
Sample_C& operator=(const Sample_C&& p_sample) {
m_k = p_sample.m_k;
m_f = p_sample.m_f;
}
void alloc(int p_k) {
m_k = p_k;
m_f = new float [m_k];
for (int i = 0; i < m_k; i++) {
m_f[i] = 2.0 + i;
}
}
private:
int m_k;
float* m_f;
};
int main() {
//默认构造函数
Sample_C A;
A.alloc(2);
//拷贝构造函数
Sample_C B(A);
//移动构造函数
Sample_C C(std::move(A));
Sample_C D;
//拷贝赋值函数
D = A;
Sample_C E;
//移动赋值函数
E = std::move(A);
return 0;
}
深拷贝和浅拷贝
需要新开辟内存空间的叫深拷贝,不需要开辟内存空间,将对象的成员值直接赋值的叫浅拷贝
//深拷贝
Sample_C(const Sample_C& p_sample) {
m_k = p_sample.m_k;
m_f = new float[m_k];
memcpy(m_f, p_sample.m_f, m_k*sizeof(float));
}
//浅拷贝
Sample_C(const Sample_C& p_sample) {
m_k = p_sample.m_k;
m_f = p_sample.m_f;
}
当成员变量中有指针的时候,使用浅拷贝会导致多个指针指向同一块内存空间,在析构的时候会出现指针悬挂的问题。