当成员变量里面有指针或者多维数组时,如何写 拷贝构造函数,移动构造函数,拷贝赋值运算符,移动赋值运算符
头文件
#pragma once
#include<iostream>
using namespace std;
const int num = 5;
/*
重写C++默认函数
*/
class DefaultRewrite
{
public:
DefaultRewrite();
// 拷贝构造函数
DefaultRewrite(const DefaultRewrite& other);
// 手动定义拷贝赋值运算符
DefaultRewrite& operator=(const DefaultRewrite& other);
// 移动构造函数
DefaultRewrite(DefaultRewrite&& other);
// 自定义移动赋值运算符
DefaultRewrite& operator=(DefaultRewrite&& other);
void initBuffer(char* copy,int size);
void printDefaultRewrite();
private:
char* m_pBuf;
int cal[num][num];
int m_bufSize;
};
实现
#include "DefaultRewrite.h"
DefaultRewrite::DefaultRewrite():
m_pBuf(nullptr), cal{}, m_bufSize(num)
{
cout << "默认构造函数" << endl;
}
DefaultRewrite::~DefaultRewrite()
{
if (m_pBuf )
{
delete[] m_pBuf;
}
m_pBuf = nullptr;
cout << "析构函数" << endl;
}
void DefaultRewrite::initBuffer(char* copy, int size)
{
int minsize = min(size, m_bufSize);
if (m_pBuf == nullptr)
{
m_pBuf = new char[m_bufSize];
}
//std::copy(copy, copy + minsize, m_pBuf);
memcpy(m_pBuf, copy, m_bufSize);
for (int i = 0; i < num; i++)
{
for (int j = 0; j < num; j++)
{
cal[i][j] = i+j;
}
}
}
// 拷贝构造函数
DefaultRewrite::DefaultRewrite(const DefaultRewrite& other)
: m_pBuf(other.m_pBuf) , m_bufSize(other.m_bufSize)
{
cout << "拷贝构造函数" << endl;
std::copy(&other.cal[0][0], &other.cal[0][0] + num * num, &cal[0][0]);
if (other.m_pBuf)
{
m_pBuf = new char[m_bufSize];
//std::copy(other.m_pBuf, other.m_pBuf + m_bufSize, m_pBuf);
memcpy(m_pBuf, other.m_pBuf, m_bufSize);
}
else
{
m_pBuf = nullptr;
}
}
// 拷贝赋值运算符
DefaultRewrite& DefaultRewrite::operator=(const DefaultRewrite& other) {
cout << "拷贝赋值运算符" << endl;
if (this == &other) return *this;
if (m_pBuf)
{
delete[] m_pBuf;
}
// 深拷贝
m_pBuf = new char[m_bufSize];
std::copy(&other.cal[0][0], &other.cal[0][0] + num * num, &cal[0][0]);
//std::copy(other.m_pBuf, other.m_pBuf + m_bufSize, m_pBuf);
memcpy(m_pBuf, other.m_pBuf, m_bufSize);
return *this;
}
// 移动构造函数
DefaultRewrite::DefaultRewrite(DefaultRewrite&& other)
: m_pBuf(other.m_pBuf), m_bufSize(other.m_bufSize)
{
cout << "移动构造函数" << endl;
other.m_pBuf = nullptr;
std::copy(&other.cal[0][0], &other.cal[0][0] + num * num, &cal[0][0]);
}
// 移动赋值运算符
DefaultRewrite& DefaultRewrite::operator=(DefaultRewrite&& other)
{
cout << "移动赋值运算符" << endl;
if (this == &other) return *this;
if (m_pBuf)
{
delete[] m_pBuf;
}
m_pBuf = other.m_pBuf;
m_bufSize = other.m_bufSize;
std::copy(&other.cal[0][0], &other.cal[0][0] + num * num, &cal[0][0]);
other.m_pBuf = nullptr;
return *this;
}
void DefaultRewrite::printDefaultRewrite()
{
if(m_pBuf)
cout << m_pBuf << endl;
for (int i = 0; i < num; i++)
{
for (int j = 0; j < num; j++)
{
cout << cal[i][j] << " ";
}
cout << endl;
}
}
main
int main()
{ DefaultRewrite obj1; // 默认构造函数
cout << "obj1" << endl;
obj1.printDefaultRewrite();
char str[] = "hi";
obj1.initBuffer(str,3);
cout << "obj1 initBuffer" << endl;
obj1.printDefaultRewrite();
DefaultRewrite obj2 = obj1; // 拷贝构造函数
cout << "obj2" << endl;
obj2.printDefaultRewrite();
DefaultRewrite obj3;
obj3 = obj1; // 拷贝赋值运算符
cout << "obj3" << endl;
obj3.printDefaultRewrite();
DefaultRewrite obj4(std::move(obj2)); // 移动构造函数
cout << "obj4" << endl;
obj4.printDefaultRewrite();
DefaultRewrite obj5;
obj5 = std::move(obj1); // 移动赋值运算符
cout << "obj5" << endl;
obj5.printDefaultRewrite();
cout << "obj1" << endl;
obj1.printDefaultRewrite();
cout << "obj2" << endl;
obj2.printDefaultRewrite();
}
运行结果
注意事项
move
在代码中调用obj3 = std::move(obj1)后,obj1之所以还可以继续被使用,主要原因是:
移动语义的转移是通过“窃取”资源的方式实现的。
-
std::move(obj1)只是将obj1转换为一个右值,本身不会做实际的资源转移。
-
随后调用DefaultRewrite的移动赋值运算符实现资源的转移。
-
但移动赋值运算符只将obj1的m_pBuf指针转移给了obj3。
-
obj1的其他成员如m_bufSize等并没有被修改。
-
所以obj1的成员中只有m_pBuf被置为空指针,其他成员依然保持有效状态。
-
这就是移动语义的“destructive move” 特性,移动后源对象可处于任意状态。
-
如果obj1的其他成员没有再次使用m_pBuf,那么obj1仍可继续正常使用。
-
但需要注意,obj1的m_pBuf已经失效,不能再被访问。
总结来说,移动语义下源对象可被部分重用,这 depends on 的是资源的转移方式。如果只转移部分资源,源对象可能依然可以使用剩余的资源。
数组初始化与赋值
cal=other.cal;为什么会报错
数组名是一个地址常量,其值和第一个元素的地址值相同,不可修改。赋值号左边必须是一个变量
https://blog.csdn.net/huang1600301017/article/details/88562653
深拷贝
数组和指针在这四个函数里面都需要手动深拷贝
在移动构造函数中,对指针成员m_pBuf我们直接进行了浅拷贝:
m_pBuf = other.m_pBuf;
移动语义
没有进行深拷贝, 是可以的。主要原因有:
-
移动构造的目的是资源的转移,而不是共享。
-
我们期望移动操作后,源对象不再使用这部分资源。
-
直接使两个对象的指针指向同一区域,即实现了资源的转移。
-
如果我们深拷贝,就会造成资源冗余,降低效率。
-
移动后源对象的指针会被置空,不会再访问这个区域。
-
所以这种“浅拷贝”方式实现了零开销的资源转移。
-
这也是移动语义的设计目的,比深拷贝更高效。
当然,如果指针指向的内容还会被源对象使用,我们就不能这样直接转移指针。
但在移动语义下,通常假设源对象资源不再被使用,所以直接指针赋值即可优化。
这是移动构造可以进行资源“窃取”的原理所在。