c++ - 返回值(引用、指针和对象)

标签 c++ optimization copy return return-value-optimization

我很难理解 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/

相关文章:

iphone - 在运行时测量和跟踪各种调用的性能的最佳方法是什么?

c++ - 类的模板化包装函数(例如 std::make_pair())` 是否被认为很慢?

python - 在 Python 中重用名称以节省内存

mysql - 将包含数据的表从一个 MySQL 服务器复制到另一个

c++ - 有没有有效的方法来获得大 vector ?

c++ - 更改 Qt 中的选项卡小部件设计

c++ - 重载运算符 C++ : Object = Object * Object

c++ - 本地版 Google Maps API

java - 在Java中将网页内容读入字符串的最佳方法是什么?

javascript - 试图用水平滚动条制作固定的表头。如何设置溢出 : auto, 但保持边距透明?