我有以下代码:
#include <stdio.h>
class Foo {
public:
int a;
~Foo() { printf("Goodbye %d\n", a); }
};
Foo newObj() {
Foo obj;
return obj;
}
int main() {
Foo bar = newObj();
bar.a = 5;
bar = newObj();
}
当我使用g++
编译并运行它时,我得到:
Goodbye 32765
Goodbye 32765
打印的数字似乎是随机的。
我有两个问题:
5
第一次不打印?我来自C语言,因此是printf
,而且在理解析构函数,何时调用析构函数以及如何从函数返回类方面遇到困难。
答案 0 :(得分:11)
让我们看看您的主要功能发生了什么:
int main() {
Foo bar = newObj();
在这里,我们只实例化一个Foo
并使用返回值newObj()
对其进行初始化。由于copy elision在这里没有调用析构函数:总结起来非常迅速,而不是将obj
复制/移动到bar
然后解构obj
,obj
是直接在bar
的存储中构建。
bar.a = 5;
这里无话可说。我们只是将bar.a
的值更改为5。
bar = newObj();
在这里bar
被复制分配了 1 返回值newObj()
,然后销毁了由该函数调用创建的临时对象 2 ,这是第一个Goodbye
。此时,bar.a
不再是5
,而是临时对象的a
中的任何内容。
}
main()
的末尾,局部变量被破坏,包括bar
,这是第二个Goodbye
,由于先前的赋值,它不打印5
。
1 由于用户定义了析构函数,因此未发生移动分配,因此未隐式声明任何移动分配运算符。
2 正如YSC在评论中提到的那样,请注意,此析构函数调用具有未定义的行为,因为它正在访问a
,该bar
在这一点上尚未初始化。出于相同的原因,a
与临时对象的分配,尤其是info Starting JS server...
info Building and installing the app on the device (cd android && gradlew.bat app:installDebug)...
> Task :app:compileDebugJavaWithJavac
error: error reading C:\Users\Joshua Uyi\.m2\repository\javax\inject\javax.inject\1\javax.inject-1.jar; error in opening zip file
error: error reading C:\Users\Joshua Uyi\.m2\repository\javax\inject\javax.inject\1\javax.inject-1.jar; error in opening zip file
> Task :app:checkDebugDuplicateClasses FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:checkDebugDuplicateClasses'.
> 1 exception was raised by workers:
java.util.zip.ZipException
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.4.1/userguide/command_line_interface.html#sec:command_line_warnings
BUILD FAILED in 15s
18 actionable tasks: 18 executed
error Could not install the app on the device, read the error above for details.
Make sure you have an Android emulator running or a device connected and have
set up your Android development environment:
https://facebook.github.io/react-native/docs/getting-started.html
error Command failed: gradlew.bat app:installDebug. Run CLI with --verbose flag for more details.
作为其一部分的分配,也具有未定义的行为。
答案 1 :(得分:2)
1)很简单,您的代码中有两个Foo
对象(在main
和newObj
中),所以有两个析构函数调用。实际上,这是您将看到的最小析构函数调用数,编译器可能会为返回值创建一个未命名的临时对象,如果这样做,您将看到三个析构函数调用。在C ++的历史中,返回值优化的规则已更改,因此您可能会或可能不会看到此行为。
2)因为调用析构函数时Foo::a
的值从不为5,所以newObj
中的值从不为5,尽管main
中的值为5,但不是时间到main
的结尾(调用析构函数的时间)。
我猜您的误解是您认为赋值语句bar = newObj();
应该调用析构函数,但事实并非如此。在分配过程中,对象将被覆盖,但不会被破坏。
答案 2 :(得分:0)
我认为这里的主要困惑之一是对象身份。
bar
总是 同一对象。将不同的对象分配给bar
时,不会破坏第一个对象,而是调用operator=(const& Foo)
(副本分配运算符)。它是five special member functions之一,可以由编译器自动生成(在这种情况下就是这种情况),并且仅用{{1}中的任何内容覆盖 bar.a
}。提供您自己的newObj().a
来查看/何时发生(并在发生此情况之前确认operator=
确实是a
)。
5
的析构函数仅被调用一次-当bar
在函数末尾超出范围时。只有一个析构函数调用-第二个bar
返回的临时函数。删除newObj()
中的第一个临时文件(在这种情况下,该语言允许使用该语言,因为实际上并没有必要创建和立即销毁它),并使用返回值{{1 }}。