You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++多类间正确传递对象避免冗余拷贝的方法

C++ 长生命周期依赖的零拷贝持有方案

首先明确场景核心特征:依赖对象(A/B类实例)初始化后不可变、生命周期覆盖整个程序运行周期,持有依赖的类不需要管理依赖对象的销毁,仅需要访问能力,同时要避免重复传参、冗余拷贝。以下是按适配度排序的落地方案:


方案1:const引用成员(最优选择,完全匹配场景需求)

引用本身就是对象的别名,不存在拷贝开销,且只要绑定的对象生命周期长于当前类实例,就不会有悬空问题,完全不需要手动管理内存:

class B
{
    const A& _objectA; // 绑定外部A实例的常量引用,无拷贝,不可修改指向的对象
public:
    double doSomethingComplicatedWithObjectA() const;
    // 构造函数传入外部A的常量引用,直接初始化成员引用
    B(const A& objectA) : _objectA(objectA) {}
    // 不需要自定义析构函数:默认生成的析构不会销毁引用绑定的外部对象,完全符合需求
};

class C
{
    const A& _objectA;
    const B& _objectB;
public:
    double doSomethingComplicatedWithObjectAandB() const;
    C(const A& objectA, const B& objectB) : _objectA(objectA), _objectB(objectB) {}
};

该方案的优势:

  • 绝对零开销,性能和反复传引用的写法完全一致,没有任何冗余拷贝
  • 语法层面强制约束依赖不可修改,匹配“实例初始化后不改动”的业务特性
  • 所有类方法可直接访问成员,不需要重复传参,代码结构干净
  • 不需要写任何自定义内存管理逻辑,不会出现内存泄漏、误删外部对象的问题

唯一限制:引用成员初始化后不能重新绑定其他对象,刚好匹配当前场景,没有负面影响。


方案2:非拥有裸指针(适配依赖需要重绑定的场景)

如果后续需要在类实例生命周期内更换指向的依赖对象,可以用const修饰的裸指针作为观测句柄。完全不需要担心析构误删的问题:只有手动调用delete才会销毁对象,默认析构只会回收指针本身占用的内存,不会触碰指针指向的外部空间。
示例代码:

class C
{
    const A* _objectA = nullptr; // 指向const A的非拥有指针,可修改指向,不可修改指向的内容
    const B* _objectB = nullptr;
public:
    double doSomethingComplicatedWithObjectAandB() const {
        // 访问前加判空逻辑即可避免空指针错误
        if (!_objectA || !_objectB) {
            // 按需求做错误处理,比如抛出异常、返回错误码
        }
        return _objectA->doSomethingComplicated() + _objectB->doSomethingComplicatedWithObjectA();
    }
    // 构造函数传入外部对象的地址初始化
    C(const A* objectA, const B* objectB) : _objectA(objectA), _objectB(objectB) {}
    // 不需要自定义析构,绝对不会误删外部对象
    // 需要换依赖时加个重置方法即可
    void resetRefs(const A* a, const B* b) {
        _objectA = a;
        _objectB = b;
    }
};

使用这类非拥有裸指针的唯一规则:永远不要在类的析构函数、任何成员函数里对这类指针调用delete,谁创建的对象谁负责销毁。为了避免后续维护混淆,可以给这类非拥有指针成员统一加_obs后缀(observer的缩写)或者加注释标注所有权归属。


关于智能指针的说明

std::shared_ptr、std::unique_ptr都属于拥有所有权的智能指针,仅在类需要负责管理依赖对象生命周期时使用:

  • 依赖对象是类动态创建、需要跟着类实例一起销毁时,用std::unique_ptr
  • 依赖对象需要在多个无明确生命周期先后顺序的实例间共享,最后一个持有者销毁时才释放对象时,用std::shared_ptr

当前场景下依赖对象生命周期全局有效,类完全不拥有所有权,用智能指针反而会增加引用计数的额外开销,完全没必要。如果需要更明确的语义标注,C++17标准提供的std::observer_ptr就是专门为非拥有观测指针设计的,行为和const裸指针完全一致,只是代码可读性更强。


针对单人开发的小建议

单人开发缺少代码评审容易积累坏实践,针对数值计算类C++项目,可以加几个低成本的检查习惯:

  • 编译时开启最高警告等级,加-Wall -Wextra -Wpedantic编译参数,大部分不必要的拷贝、悬空引用、未初始化变量问题编译器会直接告警
  • 所有初始化后不修改的成员变量、参数、引用都加const限定,从语法层面避免意外修改
  • 原有代码里给值返回类型加const(比如const double doSomething() const)属于无意义写法,值返回本身是临时对象,加const不会带来性能收益,反而会阻碍移动语义优化,可以删掉
  • 写完代码后可以简单跑下性能剖析,大数组的不必要拷贝在profiler里会非常明显,很容易定位。

内容的提问来源于stack exchange,提问作者Aleksandar Demić

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 10:18:19