这个复制构造函数是否正确?
class GPSPoint
{
private:
double lat, lon, h;
char *label;
public:
GPSPoint (const GPSPoint &p)
{
if (this != &p)
{
lat = p.lat;
lon = p.lon;
h = p.h;
if ( label != NULL )
{
delete [] label;
label = NULL;
}
if (p.label != NULL )
{
label = new char [ strlen( p.label ) + 1 ];
strcpy ( label, p.label );
}
}
}
}
答案 0 :(得分:8)
如果你班上有指针,你可能做错了。
最好将其重写为:
class GPSPoint
{
private:
double lat;
double lon;
double h;
std::string label;
public:
GPSPoint (GPSPoint const& copy)
: lat(copy.lat)
, lon(copy.lon)
, h(copy.h)
, label(copy.label)
{}
// You should also have an assignment operator
GPSPoint& operator=(GPSPoint copy) // Use Copy/Swap idum.
{ // Pass by value to get implicit copy
this->swap(copy); // Now swap
return *this;
}
void swap(GPSPoint& copy) throw()
{
std::swap(lat, copy.lat);
std::swap(lon, copy.lon);
std::swap(h, copy.h);
std::swap(label,copy.label);
}
};
现在看起来更简单了。
但是,嘿,我们忘了有编译器生成的副本构造函数:
所以它现在也简化了:
class GPSPoint
{
private:
double lat;
double lon;
double h;
std::string label;
};
完成。越简越好。
如果你必须绝对保留指针(因为你认为它是一个优化(不是),或者你需要学习指针(你这样做,但你需要学习何时不使用它们)。)
class GPSPoint
{
private:
double lat;
double lon;
double h;
char* label;
public:
// Don't forget the constructor and destructor should initialize label
// Note the below code assumes that label is never NULL and always is a
// C-string that is NULL terminated (but that each copy creates
// and maintains its own version)
GPSPoint (GPSPoint const& copy)
: lat(copy.lat)
, lon(copy.lon)
, h(copy.h)
, label(new char[strlen(copy.label)+1])
{
std::copy(copy.label, copy.label + strlen(label) + 1, label);
}
// You should also have an assignment operator
GPSPoint& operator=(GPSPoint copy) // Use Copy/Swap idum.
{ // Pass by value to get implicit copy
this->swap(copy); // Now swap
return *this;
}
void swap(GPSPoint& copy) throw()
{
std::swap(lat, copy.lat);
std::swap(lon, copy.lon);
std::swap(h, copy.h);
std::swap(label,copy.label);
}
};
答案 1 :(得分:3)
有点冗长;我会重新设计它。
更重要的是,它看起来更像是op=
而非复制构造函数,尤其是因为您为label
测试NULL
,就好像它已经被使用过一样。而且,由于它没有初始化,因此NULL
已经不可能...... delete [] label
是一个严重错误。
但如果您将其设为op=
,那么我认为这在语义上是正确的。
那么不要忘记你的构造函数(并初始化label
到NULL
!),复制构造函数(使用op=
)和析构函数。
我知道你在上一个问题(没有任何令人信服的理由)中说过你“不想使用std::string
”,但这是你为什么要这样做的完美例子。
答案 2 :(得分:1)
据我了解,您创建了operator =
的有效实现,而不是复制构造函数。
如果对象已经存在,if (this != &p)
是有意义的;删除标签
答案 3 :(得分:1)
简而言之:不。这不是一个可怕的赋值运算符,但它作为复制构造函数被破坏了。
首先,没有可能的方式进行自我分配,因为你没有分配任何东西。 this
指向构造函数开头的未初始化的内存块,而p
是您正在复制的完全构造的Point对象。两者不能重合。所以最初的检查是浪费时间。
再往下,检查以确保label
不为空。正如我所说,this
指向未初始化的内存... label
此时可以是任何值,无法判断该测试是通过还是失败。如果它确实不是null,那么你将delete[]
一个随机的内存部分。如果你很幸运,这将立即失败,但它没有。
在样式方面,首选构造函数初始化程序列表以初始化成员。
GPSPoint (const GPSPoint& copy)
: lat(copy.lat)
, lon(copy.lon)
, h(copy.h)
, label(0)
{
if(copy.label) {
label = new char[ strlen( copy.label ) + 1 ];
strcpy ( label, copy.label );
}
}
就正确性而言,摆脱c-string,并使用正确的字符串类。然后,您根本不需要实现复制构造函数。
无论如何,如果实现了复制构造函数,请确保实现了复制赋值运算符和析构函数;我认为这些都是为了简洁而遗漏的,但如果不是,那么它们非常重要。
答案 4 :(得分:0)
正如a1ex07在评论中所说,您的代码看起来更像是放在operator=
中的内容,而不是复制构造函数。
首先,复制构造函数正在初始化一个全新的对象。类似if (this != &p)
的检查在复制构造函数中没有多大意义;您传递给复制构造函数的点永远不会是您在该点初始化的对象(因为它是一个全新的对象),因此检查始终为true
。
此外,if (label != NULL)
之类的操作不起作用,因为label
尚未初始化。此检查可能会以随机方式返回true
或false
(具体取决于未初始化的label
是否NULL
)。
我会这样写:
GPSPoint(const GPSPoint& p) : lat(p.lat), lon(p.lon), h(p.h) {
if (p.label != NULL) {
label = new char[strlen(p.label) + 1];
strcpy(label, p.label);
}
else {
label = NULL;
}
}
也许让label
成为std::string
而不是C风格的char*
会是一个更好的主意,然后你可以纯粹使用初始化列表编写你的复制构造函数:
class GPSPoint {
private:
double lat, lon, h;
std::string label;
public:
// Copy constructor
GPSPoint(const GPSPoint& p) : lat(p.lat), lon(p.lon), h(p.h), label(p.label) { }
}
答案 5 :(得分:-1)
是的 - 我最初读错了。
但是,我仍然建议您使用std::string
作为标签,因为它将为您管理内存,并且复制构造函数变得更加简单(事实上,对于这种情况不需要)。