【发布时间】:2013-09-28 21:34:15
【问题描述】:
我很难理解在 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
这里发生了什么?我创建变量然后返回它并且变量被销毁,因为它是范围的结尾。编译器正在尝试通过复制构造函数复制该变量,但它已经被破坏了,这就是为什么我有随机值?那么主要的c2实际上是什么?
案例 3
当我打电话时:
MyClass* c3 = baz();
cout << c3->id << endl;
输出是:
[333] MyClass::ctor
333
这是最简单的情况?我返回一个位于堆上的动态创建的指针,因此内存被分配而不是自动释放。当没有调用析构函数并且我有内存泄漏时,就是这种情况。我说的对吗?
是否还有其他不明显的情况或事情,我应该知道要完全掌握 C++ 中的返回值? ;) 从函数返回对象的推荐方法是什么(如果有的话) - 有什么经验法则吗?
【问题讨论】:
-
MyClass c1 = foo();不是分配。这是一个复制初始化。 -
嗯,我的错。那么在所有这些情况下,
operator=都不会被调用? -
确实如此。经验法则:按值返回(并依赖于复制省略或移动语义),除非您想公开内部结构(例如
std::vector::operator[]返回引用),不要返回拥有的原始指针(即必须删除的指针) , 不要返回const对象(因为它禁止移动语义)。
标签: c++ optimization copy return return-value-optimization