在C ++中阅读了关于复制构造函数和复制赋值运算符之后,我试图创建一个简单的例子。虽然下面的代码片段显然有用,但我不确定我是否正确地实现了复制构造函数和复制赋值运算符。能不能指出是否有任何错误/改进或更好的例子来理解相关概念。
class Foobase
{
int bInt;
public:
Foobase() {}
Foobase(int b) { bInt = b;}
int GetValue() { return bInt;}
int SetValue(const int& val) { bInt = val; }
};
class Foobar
{
int var;
Foobase *base;
public:
Foobar(){}
Foobar(int v)
{
var = v;
base = new Foobase(v * -1);
}
//Copy constructor
Foobar(const Foobar& foo)
{
var = foo.var;
base = new Foobase(foo.GetBaseValue());
}
//Copy assignemnt operator
Foobar& operator= (const Foobar& other)
{
if (this != &other) // prevent self-assignment
{
var = other.var;
base = new Foobase(other.GetBaseValue());
}
return *this;
}
~Foobar()
{
delete base;
}
void SetValue(int val)
{
var = val;
}
void SetBaseValue(const int& val)
{
base->SetValue(val);
}
int GetBaseValue() const
{
return(base->GetValue());
}
void Print()
{
cout<<"Foobar Value: "<<var<<endl;
cout<<"Foobase Value: "<<base->GetValue()<<endl;
}
};
int main()
{
Foobar f(10);
Foobar g(f); //calls copy constructor
Foobar h = f; //calls copy constructor
Foobar i;
i = f;
f.SetBaseValue(12);
f.SetValue(2);
Foobar j = f = z; //copy constructor for j but assignment operator for f
z.SetBaseValue(777);
z.SetValue(77);
return 1;
}
答案 0 :(得分:14)
您的副本分配运算符未正确实施。被分配的对象泄漏其base
指向的对象。
您的默认构造函数也是错误的:它使base
和var
都未初始化,因此当您调用delete base;
时,无法知道是否有效且在析构函数中是否有效,糟糕的事情发生。
实现复制构造函数和复制赋值运算符的最简单方法是使用the Copy-and-Swap idiom。
答案 1 :(得分:2)
只有Foobar
需要自定义复制构造函数,赋值运算符和析构函数。 Foobase
不需要一个,因为编译器提供的默认行为足够好。
在Foobar
的情况下,您在赋值运算符中有泄漏。您可以通过在分配对象之前释放对象来轻松修复它,这应该足够好了。但是,如果您将第二个指针成员添加到Foobar
,您会看到事情变得复杂的时候。现在,如果在分配第二个指针时遇到异常,则需要正确清理分配的第一个指针,以避免损坏或泄漏。当你添加更多的指针成员时,事情变得比多项式更复杂。
相反,您要做的是根据复制构造函数实现赋值运算符。然后,您应该根据非抛出交换函数实现复制构造函数。阅读Copy&amp;交换习语以获取详细信息。
此外,Foobar
的默认构造函数不会默认初始化成员。这很糟糕,因为它不是用户期望的。成员指针指向任意地址,int具有任意值。现在,如果你使用构造函数创建的对象,那么你就非常接近Undefined Behavior Land。
答案 2 :(得分:1)
我有一个非常简单的补丁:
class Foobar
{
int var;
std::unique_ptr<FooBase> base;
...
这应该让你开始。
底线是:
delete
(专家见第2点)delete
(你知道的更好......)