C++函数返回局部变量对象的优化-不调用复制构造函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Windgs_YF/article/details/81739758

  先说点背景知识,调用复制构造函数的三种情况:

  1.当用类一个对象去初始化另一个对象时。

  2.如果函数形参是类对象。

  3.如果函数返回值是类对象,函数执行完成返回调用时。

  在辅导学生上机时,有同学第3点提出异议。有教材上的例题为证:

请看下面的例子:

#include <stdio.h>
#include <unistd.h>

#include <iostream>

class A
{
public:
    A() { std::cout << "A Constructor" << std::endl; }
    A(const A&) { std::cout << "A Copy Constructor" << std::endl; }
    A &operator=(const A&) { std::cout << "A operator = " << std::endl; }
    ~A() {std::cout << "A Destructor" << std::endl;}
};
class B
{
public:
    B() { std::cout << "B Constructor" << std::endl; }
    B(const B&) { std::cout << "B Copy Constructor" << std::endl; }
    ~B() {std::cout << "B Destructor" << std::endl;}
};

A getA(A ta, B tb)
{
    std::cout << "====-2" << std::endl;
    A a1;                                        //-----A Constructor
    std::cout << "====-2" << std::endl;
    return a1;                                    //-----A Copy Constructor temp=a1, A Destructor a1
}

int main()
{
    A a;                                        //----A Constructor
    B b;                                        //----B Constructor
    std::cout << "====-1" << std::endl;
    A a2 = getA(a,b);      //---- B Copy Constructor, A Copy Constructor.......(a2=temp) A Copy Constructor.....A Destructor(temp des)

   A a2;    //如果此处先定义了a2,然后再进行a2对象的再次赋值,会发生什么????而不是像上面那种直接进行定义赋值。结果见下面3图

   a2= getA(a,b);

    std::cout << "====-1" << std::endl;
    return 0;
}

编译:g++ -fno-elide-constructors moveConstruct.cpp -o moveConstruct

执行:./moveConstruct

结果:

1、这是我加了编译选项的结果: -fno-elide-constructors

编译:g++ moveConstruct.cpp -o moveConstruct

执行:./moveConstruct

结果:

2、这是我不加了编译选项的结果:

有两个问题???

A a1;  构造和析构发生在什么时候????

A a2 = getA(a,b); a2构造和析构发生在什么时候????

难道a1和a2合体了。。。

3、先定义了a2,然后再进行a2对象的再次赋值

//-----------------------------------------------------------------------------------------------------------------------------------------

  1. #include <iostream>

  2. using namespace std;

  3. class Point //Point 类的定义

  4. {

  5. public:

  6. Point(int xx=0, int yy=0)

  7. {

  8. x = xx; //构造函数,内联

  9. y = yy;

  10. }

  11. Point(const Point& p); //复制构造函数

  12. void setX(int xx)

  13. {

  14. x=xx;

  15. }

  16. void setY(int yy)

  17. {

  18. y=yy;

  19. }

  20. int getX() const

  21. {

  22. return x; //常函数(第5章)

  23. }

  24. int getY() const

  25. {

  26. return y; //常函数(第5章)

  27. }

  28. private:

  29. int x, y; //私有数据

  30. };

  31. //成员函数的实现

  32. Point::Point (const Point& p)

  33. {

  34. x = p.x;

  35. y = p.y;

  36. cout << "Calling the copy constructor " << endl;

  37. }

  38.  
  39. //形参为Point类对象的函数

  40. void fun1(Point p)

  41. {

  42. cout << p.getX() << endl;

  43. }

  44. //返回值为Point类对象的函数

  45. Point fun2()

  46. {

  47. Point a(1, 2);

  48. return a;

  49. }

  50.  
  51. //主程序

  52. int main()

  53. {

  54. Point a(4, 5); //第一个对象A

  55. Point b = a; //情况一,用A初始化B。第一次调用复制构造函数

  56. cout << b.getX() << endl;

  57. fun1(b); //情况二,对象B作为fun1的实参。第二次调用复制构造函数

  58. b = fun2(); //情况三,函数的返回值是类对象,函数返回时调用复制构造函数***************

  59. cout << b.getX() << endl;

  60. return 0;

  61. }

  证据是,在CodeBlocks中,运行结果是:

  

  而不是期望的:
  

  显然,第3种情况下,复制构造函数没有被执行。

  确定问题后,我知道道理是对的,看过的几本书,厚的、薄的,都是这么写的。会不会是编译器的差别?CodeBlocks用的是gcc。gcc开源,跟标准的变化跟得紧,莫不是第3种情况已经成了老黄历,而各种书来不及变?

  我让她到VC++6.0中运行。一会儿她的反馈,在VC++6.0中复制构造函数执行了。

  真相明白了。

  这个问题需要有个交待。

  回家后再翻各种书,无果。网络搜索,CSDN上有个贴子《函数返回值是对象,是调用了拷贝构造函数?》,其中大家给的结论,是gcc做了优化,返回值为对象时,不再产生临时对象,因而不再调用复制构造函数。

  看来不是标准发生变化。

  那如果一定想要让这个构造函数执行呢?只需让忽略gcc不要搞这个优化就行了。贴子中给了个线索,在新浪博客《命名返回值优化》。文章称通过搜索知道“这是一个称为命名返回值优化的问题,而且g++的这个编译优化竟然没有直接的关闭方法给出解决办法”。作者是用命令行工作的,他后来解决的办法,是在编译命令中加上“-fno-elide-constructors”参数,例g++ -fno-elide-constructors testReturn.cpp 。

  我的学生还处在用IDE的阶段。本文的价值来了,如何在CodeBlocks下也忽略这个优化项呢?

  在CodeBlocks中,通过菜单依次选:settings->Compiler...,在Global compiler settings部分,选择Other options,在文本框中写入“-fno-elide-constructors”,如图,然后就可以ok啦。

  

  然后,如同苦难的公主终于和王子过上了幸福的生活一样,期望的结果有了。

  

  “-fno-elide-constructors”选项起了作用,有图为证。下面中加上这个参数后,编译完看到的提示信息:

  

猜你喜欢

转载自blog.csdn.net/Windgs_YF/article/details/81739758
今日推荐