我很难理解 C++ 返回值背后真正做了什么。
让我们有以下代码:
class MyClass {
public:
int id;
MyClass(int id) {
this->id = id;
cout << "[" << id << "] MyClass::ctor\n";
}
MyClass(const MyClass& other) {
cout << "[" << id << "] MyClass::ctor&\n";
}
~MyClass() {
cout << "[" << id << "] MyClass::dtor\n";
}
MyClass& operator=(const MyClass& r) {
cout << "[" << id << "] MyClass::operator=\n";
return *this;
}
};
MyClass foo() {
MyClass c(111);
return c;
}
MyClass& bar() {
MyClass c(222);
return c;
}
MyClass* baz() {
MyClass* c = new MyClass(333);
return c;
}
我使用 gcc 4.7.3。
案例1
当我打电话时:
MyClass c1 = foo();
cout << c1.id << endl;
输出为:
[111] MyClass::ctor
111
[111] MyClass::dtor
我的理解是,在 foo
中,对象是在堆栈上创建的,然后在 return 语句时销毁,因为它是作用域的末尾。返回是通过对象复制(复制构造函数)完成的,稍后将其分配给 main 中的 c1
(赋值运算符)。如果我是对的,为什么复制构造函数和赋值运算符没有输出?这是因为 RVO 吗?
案例2
当我打电话时:
MyClass c2 = bar();
cout << c2.id << endl;
输出为:
[222] MyClass::ctor
[222] MyClass::dtor
[4197488] MyClass::ctor&
4197488
[4197488] MyClass::dtor
这是怎么回事?我创建变量然后返回它,变量被销毁,因为它是范围的末尾。编译器正在尝试通过复制构造函数复制该变量,但它已经被破坏,这就是为什么我有随机值?那么 main 中的 c2
到底是什么?
案例3
当我打电话时:
MyClass* c3 = baz();
cout << c3->id << endl;
输出为:
[333] MyClass::ctor
333
这是最简单的情况?我返回一个动态创建的指针,该指针位于堆上,因此内存已分配但不会自动释放。这是当析构函数未被调用并且内存泄漏时的情况。我说得对吗?
是否还有其他不明显的情况或事情,我应该知道以完全掌握 C++ 中的返回值? ;) 从函数返回对象的推荐方法(如果有)是什么?有什么经验法则吗?
最佳答案
我想补充一点,情况 #2 是 C++ 语言中未定义行为的情况之一,因为返回对局部变量的引用是非法的。这是因为局部变量具有精确定义的生命周期,并且通过引用返回它,您将返回对函数返回时不再存在的变量的引用。因此,您表现出未定义的行为,并且给定变量的值实际上是随机的。 As is the result of the rest of your program, since Anything at all can happen .
当您尝试执行类似操作(通过引用或通过地址返回局部变量)时,大多数编译器都会发出警告 - 例如,gcc 会告诉我这样的事情:
bla.cpp:37:13: warning: reference to local variable ‘c’ returned [-Wreturn-local-addr]
但是,您应该记住,当出现可能表现出未定义行为的语句时,编译器根本不需要发出任何类型的警告。不过,必须不惜一切代价避免像这样的情况,因为它们实际上永远不会正确。
关于c++ - 返回值(引用、指针和对象),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19071902/