C++ 中按值传递造成的损坏

问题描述 投票:0回答:2

Herbert Schildt 的《C++ IT-Tutorial》一书第 9 章第 368 页指出了以下问题:

“即使您按值将对象传递给函数,在这种情况下,理论上应该隔离和保护传递的对象,但该对象仍然可以被更改甚至被副作用破坏。当对象的析构函数被调用时,可能会发生这种情况。在函数内调用以删除对象的本地副本。”

编写该段落是为了鼓励使用复制构造函数。

不幸的是,尽管书中的大多数其他问题都用简单的示例进行了记录,但我找不到重现此行为的最小示例。你能推荐一个吗?

c++ parameter-passing destructor
2个回答
5
投票

这通常是因为程序员没有遵循三、五或零的规则而发生。

当您按值传递对象时,编译器将创建代码来进行“浅”副本。以具有指针成员变量的对象为例。复制对象将复制 pointer 而不是它指向的数据。当副本被破坏时,它delete内存,这意味着原始对象中的指针不再有效,因为它仍然指向现在已删除的内存。


举个简单的例子:

struct S { // Some data }; struct Bad { S* pointer; Bad() : pointer{ new S } // Create an object, and initialize the pointer {} ~Bad() { delete pointer; } }; void f(Bad copy) { // Do something... } int main() { Bad original; f(original); }

调用函数
f

时,会创建对象

original
的副本。该副本的生命周期为
f
的运行时。当
copy
的生命周期结束时,它的析构函数将被调用。但由于
original
copy
都指向同一个
S
对象,因此
original
中的指针将变得无效。

这个问题可以通过多种方式解决。第一个也是最天真的是创建一个
Bad

复制构造函数,它创建自己的

S
副本,因此它独立于原始版本。
更好的解决方案是使用 

std::shared_ptr<S>

之类的东西代替。这通常用于标记所有权,但也可以在这里工作。

更好的解决方案是根本不将 

S

对象作为指针,因为编译器生成的复制构造函数将确保创建

S
对象的副本。
但我认为最好的解决方案是通过引用传递对象:

void f(Bad const& reference) { ... }



0
投票

struct bar { }; void foo(bar x, bar* y) { delete y; } int main() { bar* a = new bar; bar& b = a; foo(b, a); }

foo

按值传递

b
,它不能直接通过该副本修改对象。虽然同一个函数可以通过另一个参数删除同一个对象。但我不知道这是如何或为何相关的。我无法理解最后一句话

“当在函数内调用对象的析构函数来删除对象的本地副本时,可能会发生这种情况”

在上面的示例中,这意味着
x

超出范围会以某种方式影响原始

bar
对象。虽然
x
只是一个副本,但我无法想象这怎么会发生,除非
bar
一开始就被破坏了。
    

© www.soinside.com 2019 - 2024. All rights reserved.