2019独角兽企业重金招聘Python工程师标准>>>
前言
C++的函数参数和返回分为按值传递和按引用传递,应用到类上面,会涉及到类的 赋值操作 复制函数 构造函数 析构函数
虽然java开发了两年,但对我而言c++我还只是一个初学者.c++还有很多陌生的特性需要自己亲自探索.这里用实际例子作为探索,不对之处望斧正:
由于基本类型和类在函数里的生命周期是相似的,这里以一个自定义类为例,一是为了方便观察对象的地址,二是可以了解每一步会涉及到类的哪些生命周期:
类的定义
#include <iostream>using std::cout;
using std::endl;
using std::string;class A {
public:A() {cout << "构造函数:" << this << endl;}A(const A &input) {cout << "复制函数:" << this << endl;}// 复制直接返回 *this 本身A &operator=(const A &input) {cout << "赋值函数:" << this << endl;// this 其实是个新的内存空间,啥值都没设置return *this;}~A() {cout << "析构函数:" << this << endl;}
};
值传递 和 返回值
我们定义一个值传递的函数,返回的也是值
A test1(A a) {cout << "test1函数体:&a=" << &a << endl;return a;
}
执行main函数如下
int main() {cout << "--- 对象创建 ---" << endl;A a;cout << "--- 函数调用 ---" << endl;A b = test1(a);cout << "--- 结束 ---" << endl;return 0;
}
执行结果
--- 对象创建 ---
构造函数:0x7fff5ed19768
--- 函数调用 ---
复制函数:0x7fff5ed19750
test1函数体:&a=0x7fff5ed19750
复制函数:0x7fff5ed19758
析构函数:0x7fff5ed19750
--- 结束 ---
析构函数:0x7fff5ed19758
析构函数:0x7fff5ed19768
由此可见,在函数的栈帧里,对入参a进行了一次复制(0x7fff5ed19750),而在返回栈帧里的input时,又会对函数结果复制生成一个临时变量(0x7fff5ed19758),并回收栈帧里的input. 函数结束会把临时变量返回给b,至此结束函数调用.也就是说,这里函数的入参和返回各涉及一次复制操作
函数入参和返回使用引用
我们对test1做出修改,将入参和返回变更为引用,如下
A &test1(A &a) {cout << "test1函数体:&a=" << &a << endl;return a;
}
将main中的b也变更为引用如下:
int main() {cout << "--- 对象创建 ---" << endl;A a;cout << "--- 函数调用 ---" << endl;A &b = test1(a);cout << "--- 结束 ---" << endl;return 0;
}
执行结果:
--- 对象创建 ---
构造函数:0x7fff56dd5768
--- 函数调用 ---
test1函数体:&a=0x7fff56dd5768
--- 结束 ---
析构函数:0x7fff56dd5768
因为是引用函数栈帧内并没有进行任何复制操作
函数返回的临时变量探索
如果函数不返回引用,或b不是引用,仍然会产生临时变量
A test1(A &a) {cout << "test1函数体:&a=" << &a << endl;return a;
}
A& test2(A &a) {cout << "test2函数体:&a=" << &a << endl;return a;
}
int main() {cout << "--- 对象创建 ---" << endl;A a;cout << "--- 函数调用1 ---" << endl;A b1 = test1(a);cout << "--- 函数调用2 ---" << endl;A b2 = test2(a);cout << "--- 结束 ---" << endl;return 0;
}
返回结果如下
--- 对象创建 ---
构造函数:0x7fff5daae758
--- 函数调用1 ---
test1函数体:&a=0x7fff5daae758
复制函数:0x7fff5daae748
--- 函数调用2 ---
test2函数体:&a=0x7fff5daae758
复制函数:0x7fff5daae740
--- 结束 ---
析构函数:0x7fff5daae740
析构函数:0x7fff5daae748
析构函数:0x7fff5daae758
test1和test2的区别只在于返回结果是否为引用,但在栈帧外都产生了临时变量
赋值操作探索
这里再对赋值做探索,这次把以上3中情况合在一起为例,如下
A &test1(A &a) {cout << "test1函数体:&a=" << &a << endl;return a;
}
A &test2(A a) {cout << "test2函数体:&a=" << &a << endl;return a;
}
A test3(A a) {cout << "test2函数体:&a=" << &a << endl;return a;
}
int main() {cout << "--- 对象创建 ---" << endl;A a;cout << "--- 函数调用1 ---" << endl;a = test1(a);cout << "--- 函数调用2 ---" << endl;a = test2(a);cout << "--- 函数调用3 ---" << endl;a = test3(a);cout << "--- 结束 ---" << endl;return 0;
}
输出结果
--- 对象创建 ---
构造函数:0x7fff513b6748
--- 函数调用1 ---
test1函数体:&a=0x7fff513b6748
赋值函数:0x7fff513b6748
--- 函数调用2 ---
复制函数:0x7fff513b6738
test2函数体:&a=0x7fff513b6738
赋值函数:0x7fff513b6748
析构函数:0x7fff513b6738
--- 函数调用3 ---
复制函数:0x7fff513b6728
test2函数体:&a=0x7fff513b6728
复制函数:0x7fff513b6730
赋值函数:0x7fff513b6748
析构函数:0x7fff513b6730
析构函数:0x7fff513b6728
--- 结束 ---
析构函数:0x7fff513b6748
- test1 相当于
a=a;
除了将值赋值给自己外栈帧没有额外操作,这个很好理解 - test2 对入参进行复制,并把复制的入参值赋值给a并在函数栈帧结束后回收入参
- test3 比较复杂,会复制入参和临时变量,并把临时变量赋值给a,赋值结束后对入参和临时变量进行回收