这个复制构造函数是个坏主意吗?
Is this copy constructor a bad idea?
所以,我觉得这是个坏主意:
class Foo
{
public:
Foo(const Foo& from)
{
memcpy(this, &from, sizeof(Foo));
m_someData = new int[m_dataLength];
memcpy(m_someData, from.m_someData, m_dataLength * sizeof(int));
}
private:
int* m_someData;
int m_dataLength;
};
问题是:为什么?如果 memcpy
参数的类型(和大小)相同,布局相同,那么问题在哪里?
我看到的一个潜在问题是是否存在 string
或 vector<>
字段,但我不确定这是否是一个有效的问题。
如果 Foo
可以派生自,请考虑当您有一个从 Foo
派生的 Bar
class 并复制构造一个 Foo
时会发生什么] 来自那个 Bar
实例。
如果您有任何虚拟方法,您的 sizeof(Foo)
包括编译器添加的用于实现虚拟方法(即 vtable)的任何信息,您绝对不想复制这些信息。
如果你没有任何虚方法,但是Bar
给Foo
添加了一个字段,那么你还有一个潜在的问题:那个字段可以存储在[的填充字节中=11=]。他们会被 memcpy
.
破坏
最后:
One potential issue I see is if there is a string
or vector<>
field, but I'm not sure if that's a valid concern.
为什么不呢?明明会断,而且断得很厉害
您可以通过使用默认自动生成的拷贝构造函数来避免这个问题。如果您想为其添加逻辑,而不是被迫提及要复制的所有字段,请使用隐藏数据容器:
class Foo
{
public:
Foo(const Foo& from)
{
m_Data = from.m_Data;
m_Data.m_someData = new int[m_Data.m_dataLength];
memcpy(m_Data.m_someData, from.m_Data.m_someData, m_Data.m_dataLength * sizeof(int));
}
private:
struct Data {
int* m_someData;
int m_dataLength;
// more fields
};
Data m_Data;
};
请注意这里的 m_Data = from.m_Data;
如何与您尝试使用 memcpy
达到的效果完全相同,只是它安全地做到了。
错误原因:
struct boom1 {
boom1(const boom1& from)
// reason 1: not using an initialisation list means double-initialisation of members. inefficient.
{
// reason 2: undefined behaviour on all subsequent access to
// this->_s or from._s after this line
memcpy(this, &from, sizeof(from));
// reason 3: what if someone derived from this class? didn't you just overwrite the RTTI info pointer??
}
// reason 4: c++ already generates a copy constructor that does the right thing automatically. why are you laying mines for other developers?
std::string _s;
}
"yes but my particular class is a POD and I'm in control of it"...
...直到有人从中派生、修改或维护它。
不,不,不。这是永远不对的。总是错的。确保你的学生明白这一点。有一天,他可能正在为一辆 google 汽车编程。您现在不希望它 运行 影响您的孩子,对吗?
所以,我觉得这是个坏主意:
class Foo
{
public:
Foo(const Foo& from)
{
memcpy(this, &from, sizeof(Foo));
m_someData = new int[m_dataLength];
memcpy(m_someData, from.m_someData, m_dataLength * sizeof(int));
}
private:
int* m_someData;
int m_dataLength;
};
问题是:为什么?如果 memcpy
参数的类型(和大小)相同,布局相同,那么问题在哪里?
我看到的一个潜在问题是是否存在 string
或 vector<>
字段,但我不确定这是否是一个有效的问题。
如果 Foo
可以派生自,请考虑当您有一个从 Foo
派生的 Bar
class 并复制构造一个 Foo
时会发生什么] 来自那个 Bar
实例。
如果您有任何虚拟方法,您的 sizeof(Foo)
包括编译器添加的用于实现虚拟方法(即 vtable)的任何信息,您绝对不想复制这些信息。
如果你没有任何虚方法,但是Bar
给Foo
添加了一个字段,那么你还有一个潜在的问题:那个字段可以存储在[的填充字节中=11=]。他们会被 memcpy
.
最后:
One potential issue I see is if there is a
string
orvector<>
field, but I'm not sure if that's a valid concern.
为什么不呢?明明会断,而且断得很厉害
您可以通过使用默认自动生成的拷贝构造函数来避免这个问题。如果您想为其添加逻辑,而不是被迫提及要复制的所有字段,请使用隐藏数据容器:
class Foo
{
public:
Foo(const Foo& from)
{
m_Data = from.m_Data;
m_Data.m_someData = new int[m_Data.m_dataLength];
memcpy(m_Data.m_someData, from.m_Data.m_someData, m_Data.m_dataLength * sizeof(int));
}
private:
struct Data {
int* m_someData;
int m_dataLength;
// more fields
};
Data m_Data;
};
请注意这里的 m_Data = from.m_Data;
如何与您尝试使用 memcpy
达到的效果完全相同,只是它安全地做到了。
错误原因:
struct boom1 {
boom1(const boom1& from)
// reason 1: not using an initialisation list means double-initialisation of members. inefficient.
{
// reason 2: undefined behaviour on all subsequent access to
// this->_s or from._s after this line
memcpy(this, &from, sizeof(from));
// reason 3: what if someone derived from this class? didn't you just overwrite the RTTI info pointer??
}
// reason 4: c++ already generates a copy constructor that does the right thing automatically. why are you laying mines for other developers?
std::string _s;
}
"yes but my particular class is a POD and I'm in control of it"...
...直到有人从中派生、修改或维护它。
不,不,不。这是永远不对的。总是错的。确保你的学生明白这一点。有一天,他可能正在为一辆 google 汽车编程。您现在不希望它 运行 影响您的孩子,对吗?