简体   繁体   English

返回值(引用,指针和对象)

[英]Returning value (reference, pointer and object)

I have some difficulties with understanding what is really done behind returning values in C++. 我很难理解在C ++中返回值背后的实际操作。

Let's have following code: 让我们有以下代码:

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;
}

I use gcc 4.7.3. 我使用gcc 4.7.3。

Case 1 情况1

When I call: 当我打电话时:

MyClass c1 = foo();
cout << c1.id << endl;

The output is: 输出为:

[111] MyClass::ctor
111
[111] MyClass::dtor

My understanding is that in foo object is created on the stack and then destroyed upon return statement because it's end of a scope. 我的理解是,在foo对象中会在堆栈上创建该对象,然后在return语句中将其销毁,因为它是作用域的末尾。 Returning is done by object copying (copy constructor) which is later assigned to c1 in main (assignment operator). 返回是通过对象复制(复制构造函数)完成的,该对象随后在main(赋值运算符)中分配给c1 If I'm right why there is no output from copy constructor nor assignment operator? 如果我是对的,为什么复制构造函数和赋值运算符都没有输出? Is this because of RVO? 这是因为RVO吗?

Case 2 情况二

When I call: 当我打电话时:

MyClass c2 = bar();
cout << c2.id << endl;

The output is: 输出为:

[222] MyClass::ctor
[222] MyClass::dtor
[4197488] MyClass::ctor&
4197488
[4197488] MyClass::dtor

What is going on here? 这里发生了什么? I create variable then return it and variable is destroyed because it is end of a scope. 我创建变量,然后将其返回,并且变量被销毁,因为它是作用域的结尾。 Compiler is trying copy that variable by copy constructor but It is already destroyed and that's why I have random value? 编译器正在尝试通过复制构造函数复制该变量,但是它已经被破坏了,这就是为什么我有随机值? So what is actually in c2 in main? 那么, c2实际上到底是什么?

Case 3 情况3

When I call: 当我打电话时:

MyClass* c3 = baz();
cout << c3->id << endl;

The output is: 输出为:

[333] MyClass::ctor
333

This is the simplest case? 这是最简单的情况吗? I return a dynamically created pointer which lies on heap, so memmory is allocated and not automatically freed. 我返回一个动态创建的指针,该指针位于堆上,因此内存被分配并且不会自动释放。 This is the case when destructor isn't called and I have memory leak. 当没有调用析构函数并且内存泄漏时就是这种情况。 Am I right? 我对吗?

Are there any other cases or things that aren't obvious and I should know to fully master returning values in C++? 还有其他不明显的情况或事情,我应该知道完全掌握C ++中的返回值吗? ;) What is a recommended way to return a object from function (if any) - any rules of thumb upon that? ;)从功能(如果有的话)中返回对象的推荐方法是什么-关于此的任何经验法则?

May I just add that case #2 is one of the cases of undefined behavior in the C++ language, since returning a reference to a local variable is illegal. 我可以补充一下,情况2是C ++语言中未定义行为的情况之一,因为返回对局部变量的引用是非法的。 This is because a local variable has a precisely defined lifetime, and - by returning it by a reference - you're returning a reference to a variable that does not exist anymore when the function returns. 这是因为局部变量具有精确定义的生存期,并且-通过引用返回它-您将返回对变量的引用,该变量在函数返回时不再存在。 Therefore, you exhibit undefined behavior and the value of the given variable is practically random. 因此,您表现出未定义的行为,并且给定变量的值实际上是随机的。 As is the result of the rest of your program, since Anything at all can happen . 这是程序其余部分的结果,因为一切都可能发生

Most compilers will issue a warning when you try to do something like this (either return a local variable by reference, or by address) - gcc, for example, tells me something like this : 当您尝试执行以下操作(通过引用或通过地址返回局部变量)时,大多数编译器都会发出警告-例如,gcc告诉我以下内容:

bla.cpp:37:13: warning: reference to local variable ‘c’ returned [-Wreturn-local-addr]

You should remember, however, that the compiler is not at all required to issue any kind of warning when a statement that may exhibit undefined behavior occurs. 但是,您应该记住,当发生可能表现出未定义行为的语句时,根本不需要编译器发出任何类型的警告。 Situations such as this one, though, must be avoided at all costs, because they're practically never right. 但是,必须不惜一切代价避免这种情况,因为它们实际上永远是不对的。

Case 1 : 情况1

MyClass foo() {
    MyClass c(111);  
    return c;        
}
...
MyClass c1 = foo();

is a typical case when RVO can be applied. 是可以应用RVO的典型情况。 This is called copy-initialization and the assignment operator is not used since the object is created in place, unlike the situation: 这称为复制初始化 ,不使用赋值运算符,因为对象是就地创建的,与情况不同:

MyClass c1;
c1 = foo();

where c1 is constructed, temporary c in foo() is constructed, [ copy of c is constructed ], c or copy of c is assigned to c1 , [ copy of c is destructed] and c is destructed. 其中c1构成,临时cfoo()被构造,[复制c构造], c或副本c被分配给c1 ,[复制c被破坏]和c被破坏。 (what exactly happens depends on whether the compiler eliminates the redundant copy of c being created or not). (究竟发生了什么取决于编译器是否消除了正在创建的c的冗余副本)。

Case 2 : 情况2

MyClass& bar() {
    MyClass c(222);
    return c;
}
...
MyClass c2 = bar();

invokes undefined behavior since you are returning a reference to local (temporary) variable c ~ an object with automatic storage duration. 调用未定义的行为,因为您将返回对本地(临时)变量c具有自动存储持续时间的对象的引用。

Case 3 : 情况3

MyClass* baz() {
    MyClass* c = new MyClass(333);
    return c;
}
...
MyClass c2 = bar();

is the most straightforward one since you control what happens yet with a very unpleasant consequence: you are responsible for memory management , which is the reason why you should avoid dynamic allocation of this kind always when it is possible (and prefer Case 1). 这是最直接的方法,因为您控制了所发生的事情,但是却产生了非常不愉快的结果: 您负责内存管理 ,这就是为什么应尽可能避免这种动态分配的原因(并首选案例1)。

1) Yes. 1)是的。
2) You have a random value because your copy c'tor and operator= don't copy the value of id . 2)您有一个随机值,因为您的副本c'tor和operator=不会复制id的值。 However, you are correct in assuming there is no relying on the value of an object after it has been deleted. 但是,您可以假设删除对象后不依赖任何对象的值是正确的。
3) Yes. 3)是的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM