[Effective C++]条款21:必须返回对象时,别妄想返回其reference

1、C++中,函数返回引用时可能会遇到以下三种问题

1.1、返回引用的对象是局部对象(local object)

  • 如果函数返回一个局部对象的引用,当函数执行完毕后,局部对象会被销毁,引用将指向一个无效的内存地址,导致未定义行为。
#include <iostream>

MyClass& getLocalValue() {
    MyClass obj;  // 局部变量
    return obj;    // 错误:返回局部变量的引用
}

int main() {
    MyClass& obj= getLocalValue();  // 引用指向已销毁的局部变量
    std::cout << obj << std::endl;  // 未定义行为
    return 0;
}
  • 解决方法:避免返回局部对象的引用,如果需要返回值,直接返回对象
MyClass getLocalValue() {
    MyClass obj;  
    return obj;    // 正确:返回对象的值
}

int main() {
    MyClass obj= getLocalValue();  // 正确:返回的是对象的副本
    std::cout << obj<< std::endl;  
    return 0;
}

1.2、返回new对象

  • 如果函数返回一个动态分配的对象(通过new创建),调用者需要负责释放该对象的内存。如果调用者忘了释放内存,会导致内存泄漏
#include <iostream>

int& getDynamicValue() {
    int* value = new int(42);  // 动态分配内存
    return *value;             // 返回动态分配对象的引用
}

int main() {
    int& value = getDynamicValue();  // 返回动态分配对象的引用
    std::cout << value << std::endl;  // 输出:42
    // 忘记释放内存,导致内存泄漏
    return 0;
}
  • 解决方案:避免返回动态分配对象的引用,可以使用智能指针来管理动态内存,或者直接返回值
#include <iostream>
#include <memory>

std::unique_ptr<int> getDynamicValue() {
    return std::make_unique<int>(42);  // 使用智能指针管理内存
}

int main() {
    auto value = getDynamicValue();  // 返回智能指针
    std::cout << *value << std::endl;  // 输出:42
    // 不需要手动释放内存,智能指针会自动管理
    return 0;
}

1.3、返回静态对象(static object)

  • 如果函数返回一个静态对象的引用,多次调用该函数将返回同一个对象的引用。可能导致意外的行为,尤其在多线程场景下。
#include <iostream>

int& getStaticValue() {
    static int staticValue = 42;  // 静态变量
    return staticValue;           // 返回静态变量的引用
}

int main() {
    int& value1 = getStaticValue();  // 返回静态变量的引用
    int& value2 = getStaticValue();  // 返回同一个静态变量的引用
    value1 = 100;  // 修改静态变量
    std::cout << value2 << std::endl;  // 输出:100,因为 value1 和 value2 指向同一个对象
    return 0;
}
  • 解决方法: 如果需要返回独立的对象,避免使用静态变量。可以直接返回值,返回的值是原先对象的副本。或者根据需求返回动态分配的对象。
int getStaticValue() {
    static int staticValue = 42;  // 静态变量
    return staticValue;           // 返回静态变量的值(副本)
}

int main() {
    int value1 = getStaticValue();  // 返回静态变量的副本
    int value2 = getStaticValue();  // 返回静态变量的副本
    value1 = 100;  // 修改 value1,不影响 value2
    std::cout << value2 << std::endl;  // 输出:42
    return 0;
}

2、函数返回值选择返回对象(by value)还是选择返回引用(by reference),取决于具体的引用场景和需求。

2.1、返回对象(by value)

  • 返回对象是指函数返回一个对象的副本。这种方式适用于以下场景:
    • 需要返回一个独立的对象:返回的对象在函数调用结束后仍然有效,且不会受到函数内部局部对象生命周期的影响。
    • 返回的对象较小:如果返回的对象较小(如基本类型、小型结构体等),返回对象的开销较小,适合返回对象
    • 需要返回一个临时对象:如果返回的对象是临时生成的,且不需要再函数外部修改它,返回对象也是合适的
#include <iostream>
#include <string>

std::string getGreeting() {
    std::string greeting = "Hello, World!";
    return greeting;  // 返回一个std::string对象
}

int main() {
    std::string message = getGreeting();
    std::cout << message << std::endl;
    return 0;
}

2.2、返回引用(by reference)

  • 返回引用是指返回一个对象的引用。适用以下场景:
    • 需要返回一个已存在的对象:如果函数返回的对象在函数外部已经存在,并且你希望在函数外部继续操作这个对象,返回引用是合适的选择
    • 避免不必要的拷贝:如果返回的对象较大(如大型结构体、类对象等),返回引用可以避免不必要的拷贝,提高性能。
    • 返回静态对象或全局对象:如果返回的对象是静态对象或全局对象,返回引用是安全的,因为這些对象的生命周期不会再函数调用结束后结束。
#include <iostream>
#include <string>

const std::string& getStaticGreeting() {
    static std::string greeting = "Hello, World!";
    return greeting;  // 返回一个静态std::string对象的引用
}

int main() {
    const std::string& message = getStaticGreeting();
    std::cout << message << std::endl;
    return 0;
}

2.3、返回动态分配的对象-指针(by pointer)

  • 在某些情况下,需要返回一个动态分配的对象指针。需要注意的是,调用者需要负责释放动态分配的内存,以避免内存泄漏。
#include <iostream>
#include <string>

std::string* createGreeting() {
    std::string* greeting = new std::string("Hello, World!");
    return greeting;  // 返回一个动态分配的std::string对象的指针
}

int main() {
    std::string* message = createGreeting();
    std::cout << *message << std::endl;
    delete message;  // 调用者负责释放内存
    return 0;
}

思维导图笔记:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值