C++ 带指针的拷贝构造函数
C++ copy constructor with pointers
我理解深拷贝指针的必要性(在你想要一个对象的完整拷贝的情况下),我的困惑来自于以下(完全虚构的例子)。
#include "stdafx.h"
#include <string>
class a
{
public:
a::a(std::string _sz) :
m_sz(_sz)
,m_piRandom(new int)
{
*m_piRandom = 1;
};
~a()
{
delete m_piRandom;
m_piRandom = NULL;
};
a::a(const a &toCopy)
{
operator=(toCopy);
}
a& a::operator=(const a &toAssign)
{
if (this != &toAssign)
{
m_sz = toAssign.m_sz;
if (m_piRandom)
{
// Need to free this memory!
delete m_piRandom;
m_piRandom = NULL;
}
m_piRandom = new int(*toAssign.m_piRandom);
}
return *this;
}
void SetInt(int i)
{
if (!m_piRandom)
{
m_piRandom = new int;
}
*m_piRandom = i;
}
private:
std::string m_sz;
int* m_piRandom;
};
int _tmain(int argc, _TCHAR* argv[])
{
a Orig = a("Original");
a New = a("New");
New.SetInt(9);
New = Orig;
return 0;
}
现在在我的示例中,我想测试我有一个分配了一些内存的对象的场景,在这种情况下:
a New = a("New");
New.SetInt(9); // This new's the memory
分配内存,然后当我们说: New = Orig;
我会期望内存泄漏,因为如果我盲目地新建 m_piRandom = new int(*toAssign.m_piRandom);
我会丢失它之前指向的内存。 .
所以我决定在赋值运算符中加入以下内容:
if (m_piRandom)
{
// Need to free this memory!
delete m_piRandom;
m_piRandom = NULL;
}
调用以下代码时(第一行!)a Orig = a("Original");
会导致代码崩溃,因为它调用复制构造函数(我将其称为赋值运算符以减少重复)并且指针 m_piRandom
是设置为 0xcccccccc。不为空。因此它会尝试删除从未分配的内存。我希望它在到达 New = Orig;
时能够正常工作,因为它会在分配副本之前先删除它。任何人都可以对此有所了解吗,我想我最担心的是 m_piRandom 不是 NULL,我还尝试为默认构造函数定义一个默认为 NULL 的指针,但这没有帮助。为完全人为的代码道歉..
谢谢
您的代码存在问题,您的复制构造函数未初始化您的 int 指针成员,但赋值运算符为其假定了正确的值。因此,在调用赋值运算符之前,只需在复制构造函数中将 int 指针初始化为 0。
您的第一个错误是您根据赋值运算符实现了复制构造函数。复制构造函数基于某个其他对象创建一个新对象,而赋值运算符清除并更改已创建对象的位。
所以,你正确的拷贝构造函数应该是:-
a::a(const a &toCopy) : m_sz(toCopy.m_sz), m_piRandom(new int)
{
*m_piRandom = toCopy.m_piRandom;
}
实施此操作后,您可以简化赋值运算符:
a& a::operator=(const a &toAssign)
{
if (this != &toAssign)
{
m_sz = toAssign.m_sz;
if (m_piRandom) //<<<<< No need to check this as it should always be
{ //<<<<< initialized by constructors.
delete m_piRandom;
m_piRandom = NULL;
}
m_piRandom = new int(*toAssign.m_piRandom);
}
return *this;
}
删除这些冗余后,您的赋值运算符看起来像
a& a::operator=(const a &toAssign)
{
if (this != &toAssign)
{
m_sz = toAssign.m_sz;
m_piRandom = new int(*toAssign.m_piRandom);
}
return *this;
}
发生错误是因为复制构造函数没有初始化m_piRandom
。这意味着变量将(很可能)充满垃圾(初始化对象时内存位置的任何内容)。
调用顺序是这样的:
a::a() [doesn not initialize m_piRandom] -> a::operator= -> delete m_piRandom.
修复:
a::a(const a &toCopy)
: m_piRandom{ nullptr } // <---- add this
{
operator=(toCopy);
}
Edit:您可以使用 copy&swap idiom.
大幅改进赋值运算符
我理解深拷贝指针的必要性(在你想要一个对象的完整拷贝的情况下),我的困惑来自于以下(完全虚构的例子)。
#include "stdafx.h"
#include <string>
class a
{
public:
a::a(std::string _sz) :
m_sz(_sz)
,m_piRandom(new int)
{
*m_piRandom = 1;
};
~a()
{
delete m_piRandom;
m_piRandom = NULL;
};
a::a(const a &toCopy)
{
operator=(toCopy);
}
a& a::operator=(const a &toAssign)
{
if (this != &toAssign)
{
m_sz = toAssign.m_sz;
if (m_piRandom)
{
// Need to free this memory!
delete m_piRandom;
m_piRandom = NULL;
}
m_piRandom = new int(*toAssign.m_piRandom);
}
return *this;
}
void SetInt(int i)
{
if (!m_piRandom)
{
m_piRandom = new int;
}
*m_piRandom = i;
}
private:
std::string m_sz;
int* m_piRandom;
};
int _tmain(int argc, _TCHAR* argv[])
{
a Orig = a("Original");
a New = a("New");
New.SetInt(9);
New = Orig;
return 0;
}
现在在我的示例中,我想测试我有一个分配了一些内存的对象的场景,在这种情况下:
a New = a("New");
New.SetInt(9); // This new's the memory
分配内存,然后当我们说: New = Orig;
我会期望内存泄漏,因为如果我盲目地新建 m_piRandom = new int(*toAssign.m_piRandom);
我会丢失它之前指向的内存。 .
所以我决定在赋值运算符中加入以下内容:
if (m_piRandom)
{
// Need to free this memory!
delete m_piRandom;
m_piRandom = NULL;
}
调用以下代码时(第一行!)a Orig = a("Original");
会导致代码崩溃,因为它调用复制构造函数(我将其称为赋值运算符以减少重复)并且指针 m_piRandom
是设置为 0xcccccccc。不为空。因此它会尝试删除从未分配的内存。我希望它在到达 New = Orig;
时能够正常工作,因为它会在分配副本之前先删除它。任何人都可以对此有所了解吗,我想我最担心的是 m_piRandom 不是 NULL,我还尝试为默认构造函数定义一个默认为 NULL 的指针,但这没有帮助。为完全人为的代码道歉..
谢谢
您的代码存在问题,您的复制构造函数未初始化您的 int 指针成员,但赋值运算符为其假定了正确的值。因此,在调用赋值运算符之前,只需在复制构造函数中将 int 指针初始化为 0。
您的第一个错误是您根据赋值运算符实现了复制构造函数。复制构造函数基于某个其他对象创建一个新对象,而赋值运算符清除并更改已创建对象的位。
所以,你正确的拷贝构造函数应该是:-
a::a(const a &toCopy) : m_sz(toCopy.m_sz), m_piRandom(new int)
{
*m_piRandom = toCopy.m_piRandom;
}
实施此操作后,您可以简化赋值运算符:
a& a::operator=(const a &toAssign)
{
if (this != &toAssign)
{
m_sz = toAssign.m_sz;
if (m_piRandom) //<<<<< No need to check this as it should always be
{ //<<<<< initialized by constructors.
delete m_piRandom;
m_piRandom = NULL;
}
m_piRandom = new int(*toAssign.m_piRandom);
}
return *this;
}
删除这些冗余后,您的赋值运算符看起来像
a& a::operator=(const a &toAssign)
{
if (this != &toAssign)
{
m_sz = toAssign.m_sz;
m_piRandom = new int(*toAssign.m_piRandom);
}
return *this;
}
发生错误是因为复制构造函数没有初始化m_piRandom
。这意味着变量将(很可能)充满垃圾(初始化对象时内存位置的任何内容)。
调用顺序是这样的:
a::a() [doesn not initialize m_piRandom] -> a::operator= -> delete m_piRandom.
修复:
a::a(const a &toCopy)
: m_piRandom{ nullptr } // <---- add this
{
operator=(toCopy);
}
Edit:您可以使用 copy&swap idiom.
大幅改进赋值运算符