条款21: 必须返回对象时,别妄想返回reference

本文探讨了C++中关于对象作为函数返回值时的注意事项,包括不应返回局部栈对象的引用,避免返回堆分配对象的引用以防止内存泄漏,以及不建议返回局部静态对象的引用。现代编译器会对返回局部变量进行优化,避免额外的构造和析构。同时,返回静态变量或对象一部分时,会触发新的构造。最后,通过实例展示了引用接收返回值的不同行为及其潜在风险。
摘要由CSDN通过智能技术生成

请记住

绝不要返回pointer或者reference指向一个local stack 对象(因为已经离开生命周期),
或返回reference指向一个heap-allocated对象(因为要承担释放内存的责任),
或返回pointer或者reference指向local static对象。

请记住

当你必须在返回一个reference或者object之间抉择时,你的工作就是挑出行为正确的那个。
让编译器厂商为尽可能降低成本鞠躬尽瘁吧,你可以享受你的生活。

实际上,现代编译器也做了优化。

当局部变量接收局部变量的时候,函数内的局部变量不会析构,而是直接把整个对象传出去,也不会重新执行构造。

#include <string>
#include <iostream>
using namespace std;

class AuthInfo {
    public:
    AuthInfo(int a, bool b): m_a(a), m_b(b) {
        cout << "AuthInfo is called" << endl;
    }

    AuthInfo(const AuthInfo& a) {
        m_a = a.m_a;
        m_b = a.m_b;
        cout << "copy construct is called" << endl;
    }
    AuthInfo& operator = (const AuthInfo& a) {
        m_a = a.m_a;
        m_b = a.m_b;
        cout << "operator construct is called" << endl;
        return *this;
    }
    ~AuthInfo() {
        cout << "Deonstruct is called" << endl;
    }

    private:
        int m_a;
        bool m_b;
};

AuthInfo getAuth() 
{
    AuthInfo a(1,0);
    cout << "&a = " << &a << endl;
    return a;
}

int getInt()
{
    int b = 100;
    cout << "&b = " << &b << endl;
    return b;
}

int main()
{   
    AuthInfo a1 = getAuth();  // 编译器实际是有优化的, a1没有再一次调用构造函数,直接使用了已经构造过的局部变量a,局部变量a也没有调用析构函数
    cout << "&a1 = " << &a1 << endl; // a1和局部变量a的地址完全一样.
    cout << endl;

    int b1 = getInt();  // 对应内置类型int,编译器就没有做过类似上面的优化了
    cout << "&b1 = " << &b1 << endl; // b1和局部变量b的地址是不同的
    cout << endl;
    return 0;
}

运行结果

用非引用接收非引用的局部变量,既没有发生析构,也没有发生重新构造。
在这里插入图片描述

但如果返回的是静态变量,那么会重新构造

#include <string>
#include <iostream>
using namespace std;

class AuthInfo {
    public:
    AuthInfo(int a, bool b): m_a(a), m_b(b) {
        cout << "AuthInfo is called" << endl;
    }

    AuthInfo(const AuthInfo& a) {
        m_a = a.m_a;
        m_b = a.m_b;
        cout << "copy construct is called" << endl;
    }
    AuthInfo& operator = (const AuthInfo& a) {
        m_a = a.m_a;
        m_b = a.m_b;
        cout << "operator construct is called" << endl;
        return *this;
    }
    ~AuthInfo() {
        cout << "Deonstruct is called" << endl;
    }

    private:
        int m_a;
        bool m_b;
};

AuthInfo getStaticAuth(bool staticFlag)
{
    if (staticFlag) {
        static AuthInfo staticA(46,0);
        cout << "&staticA = " << &staticA << endl; // 返回静态变量
        return staticA;
    } else {
        AuthInfo a(1,0);
        cout << "&a = " << &a << endl;
        return a;
    }
}

int main()
{   

    AuthInfo a2 = getStaticAuth(true);  // 返回的是static变量,会调用构造函数,重新生成
    cout << "&a2 = " << &a2 << endl; 
    cout << endl;

    AuthInfo a3 = getStaticAuth(false);  // 返回的是局部变量,会直接被a3接收,不会重新析构和构造
    cout << "&a3 = " << &a3 << endl; 
    cout << endl;

    return 0;
}

如果返回的是一个对象中的一部分,那么也会重新构造。就算是引用接收也没有用。

struct AuthContainer
{
    AuthInfo auth;
    int int_temp;
    AuthContainer(int a, bool b, int c) : auth(a,b), int_temp(c)
    {

    };
};


AuthInfo getAuthFromAuth()
{
    AuthInfo a(789,0); // 构造
    cout << "address of a is : " << &a << endl;
    return a;
}

AuthInfo getAuthFromAStruct()
{
    AuthContainer b(0,1,2);
    cout << "address of b.auth is : " << &b.auth << endl;
    return b.auth;
}

int main() {
    AuthInfo b1 = getAuthFromAStruct(); // 非引用接收会重新构造
    cout << "address of b1 is : " << &b1 << endl;

    const AuthInfo& b2 = getAuthFromAStruct(); // 引用接收也会重新构造
    cout << "address of b2 is : " << &b2 << endl;
    return 0;
}

运行结果

在这里插入图片描述

局部变量不能返回引用

  1. 首先会有编译告警
  2. 其次局部变量会析构调。
#include <string>
#include <iostream>
using namespace std;

class AuthInfo {
    public:
    AuthInfo(int a, bool b): m_a(a), m_b(b) {
        cout << "AuthInfo is called" << endl;
    }

    AuthInfo(const AuthInfo& a) {
        m_a = a.m_a;
        m_b = a.m_b;
        cout << "copy construct is called" << endl;
    }
    AuthInfo& operator = (const AuthInfo& a) {
        m_a = a.m_a;
        m_b = a.m_b;
        cout << "operator construct is called" << endl;
        return *this;
    }
    ~AuthInfo() {
        cout << "Deonstruct is called" << endl;
    }

    private:
        int m_a;
        bool m_b;
};

AuthInfo& getAuth() // 局部变量返回引用
{
    AuthInfo a(1,0);
    cout << "&a = " << &a << endl;
    return a;
}

int main()
{   
    AuthInfo& a1 = getAuth();  
    cout << "&a1 = " << &a1 << endl; 
    cout << endl;

    return 0;
}

运行结果

在这里插入图片描述

上面的例子是使用引用接收返回,如果不用引用接收返回值,那么就是用一个已经析构的对象,去初始化对象。

#include <string>
#include <iostream>
using namespace std;

class AuthInfo {
    public:
    AuthInfo(int a, bool b): m_a(a), m_b(b) {
        cout << "AuthInfo is called" << endl;
    }

    AuthInfo(const AuthInfo& a) {
        m_a = a.m_a;
        m_b = a.m_b;
        cout << "copy construct is called" << endl;
    }
    AuthInfo& operator = (const AuthInfo& a) {
        m_a = a.m_a;
        m_b = a.m_b;
        cout << "operator construct is called" << endl;
        return *this;
    }
    ~AuthInfo() {
        cout << "Deonstruct is called" << endl;
    }

    private:
        int m_a;
        bool m_b;
};

AuthInfo& getAuth() // 局部变量返回引用
{
    AuthInfo a(1,0);
    cout << "&a = " << &a << endl;
    return a;
}

int main()
{   
    AuthInfo a1 = getAuth();   // 用非引用接收引用
    cout << "&a1 = " << &a1 << endl; 
    cout << endl;

    return 0;
}

运行结果

用已经析构的对象初始化函数接收对象
在这里插入图片描述

有一种情况是用引用变量去接收return by value,从运行结果看,和使用value去接收value的情况一直,既不会发生析构,也不会发生重新构造。 不知这种用法是否是推荐的。

#include <string>
#include <iostream>
using namespace std;

class AuthInfo {
    public:
    AuthInfo(int a, bool b): m_a(a), m_b(b) {
        cout << "AuthInfo is called" << endl;
    }

    AuthInfo(const AuthInfo& a) {
        m_a = a.m_a;
        m_b = a.m_b;
        cout << "copy construct is called" << endl;
    }
    AuthInfo& operator = (const AuthInfo& a) {
        m_a = a.m_a;
        m_b = a.m_b;
        cout << "operator construct is called" << endl;
        return *this;
    }
    ~AuthInfo() {
        cout << "Deonstruct is called" << endl;
    }

    private:
        int m_a;
        bool m_b;
};

AuthInfo getAuth() // 局部变量返回非引用
{
    AuthInfo a(1,0);
    cout << "&a = " << &a << endl;
    return a;
}

int main()
{   
    const AuthInfo& a1 = getAuth();   // 用引用接收非引用
    cout << "&a1 = " << &a1 << endl; 
    cout << endl;

    return 0;
}

在这里插入图片描述

总结

  1. local对象不能返回引用。
  2. local对象返回值的时候,使用值接收,编译器会有优化,既不会发生局部变量的析构,也不会发生接收值的构造。
  3. static的local对象返回值的时候,使用值接收,会发生接收值的重新构造。
  4. 特别的,local对象返回值的时候,可以用const的引用类型接收,这个过程中既不会发生局部变量的析构,也不会发生接收值的构造,不知是为何?? 不知道是否是一种推荐的做法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值