关于c++11的一些特性(1) 右值引用&返回值优化

Tags:

先贴一段代码(在vs2015编译通过):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <stdio.h>
#include <utility>


class Item {
public:
    char name;
    int val;
public:
    ~Item(){
        printf("[dtor called] (%c, %i) \n", name, val);
    }
    Item():name('_'), val(0) {
        printf("[default ctor called] \n");
    }
    Item(char n, int v):name(n), val(v) {
        printf("[ctor called] (%c, %i) \n", name, val);
    }
    Item(const Item& a) {
        printf("[copy ctor called] (%c, %i) -> (%c, %i)\n", a.name, a.val, name, val);
        name = a.name;
        val = a.val;
    }
    Item& operator = (const Item& a)
    {
        printf("[operator = called] (%c, %i) -> (%c, %i)\n", a.name, a.val, name, val);
        name = a.name;
        val = a.val;
        return *this;
    }
    Item& operator = (Item&& a)
    {
        printf("[move operator = called] (%c, %i) -> (%c, %i)\n", a.name, a.val, name, val);
        name = a.name;
        val = a.val;
        return *this;
    }
};

Item test1(void)
{
    return Item('a', 1);
}

Item test2(void)
{
    Item a('b',2);
    return a;
}

Item&& test3(void)
{
    Item a('c', 3);
    return std::move(a);
}

Item test4(void)
{
    Item a('d', 4);
    return std::move(a);
}

Item test5(void)
{
    Item&& e = Item('e', 5);
    return e;
}

int main() {
    printf("----------Item a = test1()   --------------\n");
    Item a = test1();
    printf("----------Item&& b = test2() --------------\n");
    Item&& b = test2();
    printf("----------Item&& c = test3() --------------\n");
    Item&& c = test3();
    printf("----------Item&& d = test4() --------------\n");
    Item&& d = test4();
    printf("--------- Item&& e = test5() --------------\n");
    Item&& e = test5();
    printf("\n----------      test end  --------------\n\n");
    return 0;
}

Debug运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
----------Item a = test1()   --------------
[ctor called] (a, 1)
----------Item&& b = test2() --------------
[ctor called] (b, 2)
[copy ctor called] (b, 2) -> (? -858993460)
[dtor called] (b, 2)
----------Item&& c = test3() --------------
[ctor called] (c, 3)
[dtor called] (c, 3)
----------Item&& d = test4() --------------
[ctor called] (d, 4)
[copy ctor called] (d, 4) -> (? -858993460)
[dtor called] (d, 4)
--------- Item&& e = test5() --------------
[ctor called] (e, 5)
[copy ctor called] (e, 5) -> (? -858993460)
[dtor called] (e, 5)

----------      test end  --------------

[dtor called] (e, 5)
[dtor called] (d, 4)
[dtor called] (b, 2)
[dtor called] (a, 1)

从结果可以总结出:

  • test1函数才是最正确的写法,胡乱使用&&符号反而多调用了几次函数。
  • 对比test2、test1会发现,如果要返回一个函数临时变量,最好写成匿名变量的形式(test1),这样的写法编译器才能够做返回值优化
  • 其中test3的写法是错误的,看test3的打印以及test end之后的输出可以发现,变量c并没有被成功取到函数之外,c在test3返回时就被析构了。
  • test2、test4、test5几乎是一样的(根据打印来看的话),但test2是三者中最简洁的写法。

Release运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
----------Item a = test1()   --------------
[ctor called] (a, 1)
----------Item&& b = test2() --------------
[ctor called] (b, 2)
----------Item&& c = test3() --------------
[ctor called] (c, 3)
[dtor called] (c, 3)
----------Item&& d = test4() --------------
[ctor called] (d, 4)
[copy ctor called] (d, 4) -> (<, 3805970)
[dtor called] (d, 4)
--------- Item&& e = test5() --------------
[ctor called] (e, 5)
[copy ctor called] (e, 5) -> (? 1)
[dtor called] (e, 5)

----------      test end  --------------

[dtor called] (e, 5)
[dtor called] (d, 4)
[dtor called] (b, 2)
[dtor called] (a, 1)

对比Debug版的输出,会发现test2函数被编译优化成test1了。

当然,还是尽量把代码写成test1的写法吧。简洁得多。

(未经授权禁止转载)
Written on October 30, 2015

写作不易,您的支持是我写作的动力!