下面是我的代码。一旦A的构造函数完成,它会立即调用~B并删除分配的变量。我有复制构造函数和赋值构造函数。我应该执行第五条规则来防止这种情况发生吗?
编辑:我已经编辑了我的复制和赋值构造函数实现,但析构函数仍在被调用。
class B
{
public:
C **table;
B()
{
table = new C *[TABLE_SIZE]();
}
B(const B& other)
{
table = new C *[TABLE_SIZE];
memcpy(table, other.table, sizeof(C *)* TABLE_SIZE);
}
B& operator = (const B& other)
{
if (this == &other)
{
return *this;
}
delete[] table;
table = new C *[TABLE_SIZE];
memcpy(table, other.table, sizeof(C *)* TABLE_SIZE);
return *this;
}
~B()
{
delete[] table;
}
}
class A
{
protected:
B funcA();
private:
B _b;
}
A::A()
{
this->_b = this->funcA();
// calls ~B here and destroys table
}发布于 2015-03-19 05:10:52
您确实实现了复制构造函数和复制赋值运算符,但它们不正确。您实现它们的目的完全是为了执行默认值所做的事情,对于拥有存储在原始指针中的动态分配内存的情况来说,这当然是错误的。当然,第三条规则意味着您必须始终如一地实现它们。
在您的示例中,这很可能意味着复制操作必须对C对象进行深度复制:
class B
{
public:
C *table;
B() : table(new C()) {}
B(const B& other) : table(new C(*other.table)) {}
B& operator = (const B& other)
{
if (this == &other)
{
return *this;
}
delete table;
table = new C(*other.table);
return *this;
}
~B()
{
delete table;
}
};请注意,您的默认构造函数甚至不能编译-您将一个C*赋值为一个C**。我更改了上面的代码,使用单个分配(和单项delete)而不是数组。如果你有一个数组,原则保持不变。
当然,最好遵循零规则而不是三规则--如果可能的话,使用一个合适的智能指针。如果你的实际用例是一个数组,那就是把table变成一个std::vector<C>。这样你就不需要提供复制操作或析构函数了。
如果您实际上希望在B的所有副本之间共享一个C,则需要使用适当的共享所有权智能指针,例如std::shared_ptr<C>。
发布于 2015-03-19 07:45:32
简单地说,funcA返回的临时实例应该被销毁,这是正常的,也是意料之中的。代码不能正常工作的事实意味着复制构造函数和赋值运算符不能正常工作。
但更根本的是,你违反了几条比三条规则更重要的规则。您未能重用现有的std::vector解决方案。你将内存管理和B在家时做的其他事情捆绑在一起,这违反了SRP。你没有使用RAII来管理你的内部资源--一个很大的异常安全失败。
基本上,核心问题是您甚至试图执行这些操作。不要,只要使用std::vector<C*>并让编译器和标准库供应商为您实现它们即可。它们将为您节省大量代码,此外,它们将以我猜您还不知道存在的方式正确,比如异常安全。三的规则和五的规则(甚至还没有存在足够长的时间成为规则)都比零的规则差得多。
下面是正确实现的B:
class B {
public:
std::vector<C*> table;
B() : table(TABLE_SIZE) {}
};很简单,不是吗?
顺便说一句,我不知道你的学习材料是从哪里来的,但你应该烧掉任何严重过时的资源,以至于建议进行自我赋值检查,当然,还有任何代码。我不妨告诉你,640k的内存就是你所需要的全部。
发布于 2015-03-19 07:57:53
你的构造函数是:
A::A()
{
this->_b = this->funcA();
// calls ~B here and destroys table
}注释是正确的,尽管被销毁的表是临时对象中的表,而不是_b的表。由于您修复了复制赋值操作符,这意味着_b的表很好用。
执行此构造函数的步骤列表如下:
调用_b
funcA的B_b赋值运算符类型的临时对象。可以通过直接提供funcA()作为_b的初始化器来改进构造函数,而不是先默认构造_b,然后使用其赋值运算符。(但仍然存在临时对象销毁)。
在从函数返回的过程中,也可能会创建和销毁另一个临时对象,这取决于您的编译器设置。(通常不会有)。
https://stackoverflow.com/questions/29132644
复制相似问题