C++子类中写移动构造函数应该注意的地方。
从other中“窃取”资源(如指针、文件句柄等)。将other的状态置为有效但可析构的状态(例如将其指针设为nullptr释放当前对象的资源。从other中“窃取”资源。将other的状态置为有效但可析构的状态。noexcept移动操作通常不应抛出异常,这对标准库容器(如)的优化很重要。自己deepseek一下。
什么是移动构造函数(自己deepseek一下)
关键步骤:
-
实现移动构造函数
语法:test(test&& other) noexcept;
-
从
other
中“窃取”资源(如指针、文件句柄等)。 -
将
other
的状态置为有效但可析构的状态(例如将其指针设为nullptr
)。
-
-
实现移动赋值运算符
语法:test& operator=(test&& other) noexcept;
-
释放当前对象的资源。
-
从
other
中“窃取”资源。 -
将
other
的状态置为有效但可析构的状态。
-
-
标记为
noexcept
移动操作通常不应抛出异常,这对标准库容器(如std::vector
)的优化很重要。 -
..........................................................................自己deepseek一下
看例子,有两个类:
类_Str:
/// <summary>
/// 字符串类
/// </summary>
/// <typeparam name="T"></typeparam>
/// 创建时间: ????-??-?? 最后一次修改时间:2022-11-13
template<typename T>
class _Str //不要继承任何类
{
public:
using size_type = size_t;
using value_type = T;
protected:
T* _pData = null; //指针,指向第一个元素
size_t _nLength = 0; //无素个数
size_t _nBuffer = 0; //剩余缓冲区大小
size_t _nDefaultBuffer = 8; //每次分配内容多分配缺省缓冲区大小
size_t _nAutoBufferCount = 0; //自动设置缓冲次数的计数器
_Str的初化函数:
/// <summary>
/// 初始化数据,并设置缓冲区大小,如果设置bInitValue == true,
/// 则所有缓冲都会用 tValue填充,这时长度是:nBuffer。
/// </summary>
/// <param name="nBuffer"></param>
/// <param name="tValue"></param>
/// <param name="bInitValeu"></param>
/// 创建时间: ????-??-?? 最后一次修改时间:2024-04-21
inline void initData(const size_t& nBuffer,const T& tValue = 0, const bool bInitValue = false)
{
#ifdef _STR_DEBUG_
_cout << _t("_Str<T>:\t inline void initData(const int& nBuffer) 参数:") << _geti(nBuffer) << _t("\n");
#endif
if (bInitValue){
_nLength = nBuffer;
_nBuffer = 0;
_pData = _Memory::New<T>(_nLength + 1);
for (int i = 0; i < _nLength; ++i){
_pData[i] = tValue;
}
_pData[_nLength] = 0;
}else{
_nLength = 0;
_nBuffer = nBuffer;
_pData = _Memory::New<T>(_nBuffer + 1);
_pData[0] = 0;
}
}
类_StrW:
class _StrW : public _Str<wchar_t>
{
public:
看他们的两个构造函数,完全一样的代码,一个会造成内存泄露,一个不会,你能看出问题来吗?
代码1
/// <summary>
/// 移动构造函数,std::move 的本质 它只是将左值强制转换为右值引用
/// (static_cast<T&&>),本身不执行任何移动操作。
/// </summary>
/// <param name="other"></param>
/// 创建时间: 2025-04-21 最后一次修改时间:2025-04-21
inline _Str<T>(_Str<T>&& other) noexcept
{
_pData = other._pData; //指针,指向第一个元素
_nLength = other._nLength; //无素个数
_nBuffer = other._nBuffer; //剩余缓冲区大小
_nDefaultBuffer = other._nDefaultBuffer; //每次分配内容多分配缺省缓冲区大小
_nAutoBufferCount = other._nAutoBufferCount; //自动设置缓冲次数的计数器
other.initData(0);
}
代码2
/// <summary>
/// 移动构造函数,std::move 的本质 它只是将左值强制转换为右值引用
/// (static_cast<T&&>),本身不执行任何移动操作。
/// </summary>
/// <param name="other"></param>
/// 创建时间: 2025-04-21 最后一次修改时间:2025-04-21
inline _StrW( StrW&& other) noexcept
{
_pData = other._pData; //指针,指向第一个元素
_nLength = other._nLength; //无素个数
_nBuffer = other._nBuffer; //剩余缓冲区大小
_nDefaultBuffer = other._nDefaultBuffer; //每次分配内容多分配缺省缓冲区大小
_nAutoBufferCount = other._nAutoBufferCount; //自动设置缓冲次数的计数器
other.initData(0);
}
本来第二个函数应该这样写的,
/// <summary>
/// 移动构造函数,std::move 的本质 它只是将左值强制转换为右值引用
/// (static_cast<T&&>),本身不执行任何移动操作。
/// </summary>
/// <param name="other"></param>
/// 创建时间: 2025-04-21 最后一次修改时间:2025-04-21
inline _StrW(_StrW&& other) noexcept : _Str<wchar_t>(std::move(other)) {}
但我匆匆忙忙直接把父类的代码复制过来了,后来发现有内存
泄露,对着下面几行代码检查了半天,搞得都快怀疑人生了,无
论如何也没发现问题?
_pData = other._pData; //指针,指向第一个元素
_nLength = other._nLength; //无素个数
_nBuffer = other._nBuffer; //剩余缓冲区大小
_nDefaultBuffer = other._nDefaultBuffer; //每次分配内容多分配缺省缓冲区大小
_nAutoBufferCount = other._nAutoBufferCount; //自动设置缓冲次数的计数器
other.initData(0);
后来终于发现了问题,_pData先被父类初始化了,而我没有释放内存,感谢deepseek,它用
这句话提醒了我。
1. 未清理当前对象的原有内存
移动构造函数在接管 other
的资源前,没有释放 this
对象可能已持有的内存。如果 this
已经指向某个内存块,直接覆盖指针会导致内存泄漏。
附:用自己的new 和 delete统计申请的内存和释放的内存数量
例如:
//全局内存对象计数器
static size_t _obj_used;
static size_t _mem_used;
/// <summary>
/// 功能:释放内存,并把内存指针重置为0
///
/// 条款3:尽量以 new 和 delete 取代malloc 和 free
/// 分配内存,给对象分配内存不能用C语言的malloc函数, 因为 malloc函数不会调用用对象的
/// 构造函数,free函数不会调用对象的析构函数。
/// 条款5:使用相同形式的 new 和 delete
/// 游戏规则:如果你在调用 new 时使用了 [ ] ,则你必须在调用 delete 时也使用[] 。
/// 如果你在调用 new 的时候没有使用[],那么你也不应该在调用 delete 时使用[] 。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="pMemory"></param>
/// <param name="nCount"></param>
/// 创建时间:????-??-?? 最后一次修改时间:2024-11-29
template<typename T> static void Delete(T* pMemory, int nCount)
{
_obj_used -= nCount;
_mem_used -= sizeof(T) * nCount;
delete[] pMemory;
}
/// <summary>
/// 功能:分配内存。
///
/// 条款3:尽量以 new 和 delete 取代malloc 和 free
/// 分配内存,给对象分配内存不能用C语言的malloc函数, 因为 malloc函数不会调用用对象的
/// 构造函数,free函数不会调用对象的析构函数。
/// 条款5:使用相同形式的 new 和 delete
/// 游戏规则:如果你在调用 new 时使用了 [ ] ,则你必须在调用 delete 时也使用[] 。
/// 如果你在调用 new 的时候没有使用[],那么你也不应该在调用 delete 时使用[] 。
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="nCount">分配个数</param>
/// <returns></returns>
/// 创建时间: ????-??-?? 最后一次修改时间:2024-12-08
template<typename T> static T * New(const size_t& nCount, const bool& bZero = false)
{
if (nCount <= 0) return null;
if (nCount > _memory_allow_max){
throw "超出最大充许申请的对象数";
}
_obj_used += nCount;
_mem_used = _mem_used + sizeof(T) * nCount;
if (bZero) {
return new T[nCount]();
}else {
return new T[nCount];
}
}
更多推荐
所有评论(0)