今天碰到一件令我百思不得其解的问题:为什么拷贝构造函数不按自己所预期的结果输出?按 C++ 的语法来说,本该如此,并非自己理解有误而导致的。
今天碰到一件令我百思不得其解的问题:为什么拷贝构造函数
不按自己所预期的结果输出?
按 C++ 的语法来说,本该如此,并非自己理解有误而导致的!
功夫不负有心人,经过几天的搜索🔍、学习👨💻,我总算明白并解决了这个问题,特此输出该文记录一下。
📈 背景知识
1. 构造函数
💛创建并初始化类的数据成员时调用
2. 析构函数
💚当对象生命周期终止时调用,用于释放对象占有的资源
3. 拷贝构造函数
❤调用时机:
- 将某个对象用于初始化另一个新创建的对象时
- 当对象作为参数传递给函数,且函数形参为普通对象时(因为引用对象不会调用拷贝构造函数)
- 对象作为函数的返回值时
💙注意:
- 如果在类中没有定义拷贝构造函数,编译器会自行定义一个;
- 如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。
🌄 进入正题
先来看一段包含构造函数、析构函数、拷贝构造函数的简单代码,代码中穿插着许多注释,这里就不再一一解释。
本文旨在探索
拷贝构造函数
,构造函数与析构函数仅为顺带学习而提及,可略过这二者。顺带一提,注释中标注的各类函数调用顺序仅针对于预期结果。
1 |
|
运行结果
1 | Point() |
预期结果
🛕各类函数的调用顺序均已在注释中标明!
1 | Point() |
🤨 分析原因
浅浅分析下运行结果与预期结果之间拷贝构造函数调用的差异。
如果你感兴趣,可以自己去调试下,最后发现问题出在 情况 3: Point point3 = returnPoint();
处,也就是函数的返回值为对象时。
为什么?!
🚀原来是 GCC 做了优化,当返回值为对象时,不再产生临时对象,因此不再调用拷贝构造函数。
再来对比下两个结果,可见直接把 2 个拷贝构造函数都优化掉了,
⭐这时候又会有人问了:诶,为什么是 2 个,情况 3 不就是对象作为函数返回值吗?不就只会调用 1 次拷贝构造函数吗?
1 | // ... |
就这部分代码而言,我的猜想是这样的:
returnPoint()
函数返回对象时,将其拷贝到一个临时对象temp
中(① 调用拷贝构造函数),然后释放函数中的局部对象;- 当执行到
Point point3 = returnPoint();
时,将对象赋值给point3
(② 再次调用拷贝构造函数),并释放临时对象temp
,最后释放point3
对象。
差不多是这么回事
当然以上没有很严谨的科学依据,但是经过我几番调试,输出结果也吻合,估计是八九不离十!
🌍 解决办法
Q:如果一定想要让拷贝构造函数在这种情况下执行呢?
A:只需要让 GCC 不要优化:在编译命令中加入 -fno-elide-constructors
参数,例如 g++ -fno-elide-constructors CopyConstructor.cpp
.
我个人是使用的 C++ IDE 是 CLion ,如下也给出相应的解决办法。
因为使用 IDE 就是为了快速编译运行,不可能每次都执行相应代码来运行程序,所以需要配置。
只需在 CMakeLists.txt
中添加如下代码:
1 | # 添加编译选项! ==> 防止g++优化导致"返回对象不调用拷贝构造函数"! |
🗺提醒一下:如果你的代码依赖于拷贝构造函数的副作用,那么你的代码就写得很烂。你编写的拷贝构造函数就应该保证这样的优化是安全的。
⛵ 最后
gcc 和 g++ 是什么,有什么区别?
发展至今,GCC 编译器的功能也由最初仅能编译 C 语言,扩增至可以编译多种编程语言,其中就包括 C++ 。
除此之外,当下的 GCC 编译器还支持编译 Go、Objective-C,Objective-C ++,Fortran,Ada,D 和 BRIG(HSAIL)等程序,甚至于 GCC 6 以及之前的版本还支持编译 Java 程序。
那么,在已编辑好 C 语言或者 C++ 代码的前提下,如何才能调用 GCC 编译器为我们编译程序呢?很简单,GCC 编译器已经为我们提供了调用它的接口,对于 C 语言或者 C++ 程序,可以通过执行 gcc
或者 g++
指令来调用 GCC 编译器。
值得一提的是:实际使用中我们更习惯使用 gcc
指令编译 C 语言程序,用 g++
指令编译 C++ 代码。需要强调的一点是,gcc
指令也可以用来编译 C++ 程序,同样 g++
指令也可以用于编译 C 语言程序。
⭐总结:
- gcc 是 GCC 中的 GUN C Compiler(C 编译器)
- g++ 是 GCC 中的 GUN C++ Compiler(C++编译器)
CMakeLists.txt 超傻瓜式教程
CMake 命令官网:cmake.org
1 | # 本CMakeLists.txt的project名称 |