类在函数参数、返回值和赋值运算时的传递方式
参考资料:
- c++拷贝构造函数详解:https://www.cnblogs.com/alantu2018/p/8459250.html
- C++:类中的赋值函数:https://www.cnblogs.com/duwenxing/p/7445927.html
- C/C++ 引用作为函数的返回值:https://blog.youkuaiyun.com/weixin_40539125/article/details/81410008
- C++ 指针的引用和指向引用的指针:https://blog.youkuaiyun.com/fatfish_/article/details/86768887
- C++临时变量的生命周期:https://www.cnblogs.com/catch/p/3251937.html
- [c++11]我理解的右值引用、移动语义和完美转发:https://www.jianshu.com/p/d19fc8447eaa
- 《C++ Primer Plus》
类除了默认构造函数与默认析构函数外,还有默认拷贝函数和默认赋值函数。
而函数在按值传递参数和返回值的时候,会先通过拷贝的方式创建临时变量(类似test T2(T1);
),用作形参和返回值。引用参考文献的话:临时变量的作用域短暂(在C++标准中,临时变量或对象的生命周期在一个完整的语句表达式结束后便宣告结束,也就是在语句T2 = fn3(T1);
之后)。
正如《C++ Primer Plus》提到的,引用更接近const指针(都需要创建时初始化)。所以函数按引用传递参数和返回值的时候,不会创建临时变量,也没有上述的拷贝过程。
所以当类作为函数参数或返回值的时候,如果需要创建临时变量,会调用默认拷贝函数(默认直接复制所有类成员)。一般情况下这样做没什么,但是如果类数据成员有指针的话,就会出现如下等价情况
T3.p = new int(985);
T4.p = T3.p;
然后T3与T4在各自的作用域结束时会被析构,在T3中,由于使用了new创建内存,所以析构函数里需要
delete p;
同理T4时T3的完整复制,所以T4有相同的析构函数,所以析构时会出现以下等价情况
delete T3.p;
delete T4.p;
因为大多数编译器也不会报错,所以这样会造成致命的隐藏bug。(个人猜想第一次delete释放了不该释放的内存,第二次delete可能造成数据错乱)
同理,把函数返回值赋值给别的类时,也会调用默认赋值函数。和默认拷贝函数一样,也是直接复制所有类成员,同样是如果数据成员有指针的话,会触发以上隐藏bug
那么具体操作时如何避免以上问题呢,我们先看如下例子,再慢慢解释
// test.h
// 防止头文件重包含
#ifndef test_h
#define test_h
#include <assert.h>
#include <iostream>
using namespace std;
class test_empty {};
class test {
private:
double weight = 3.0;
public:
double area = 9.0;
int* p = NULL;