c++ 函数的值传递,引用传递 和 引用返回的探索

C++函数参数传递机制
本文通过实例详细探讨了C++中函数参数的值传递与引用传递机制,并深入分析了这两种方式下对象的生命周期及复制构造函数、赋值操作等细节。

前言

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,赋值结束后对入参和临时变量进行回收

转载于:https://my.oschina.net/tdONEmadao/blog/915677

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值