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

C++使用建造者模式时如何避免多次调用构造函数

问题根因

你遇到的拷贝构造触发,是因为add_field返回左值引用,即使调用方是临时右值,返回值仍是左值,拷贝初始化时无法触发移动语义,只能执行拷贝。你尝试的左值引用绑定临时对象的写法属于未定义行为,临时对象在表达式结束后即析构,引用悬空会导致乱码,不可用。

以下是可落地的优化方案,均可实现仅一次有效构造、无多余拷贝的效果:

方案1:拆分构造与链式调用(零额外开销,最易实现)

直接先在栈上构造目标对象,再调用链式方法,完全避免临时对象产生,全程只会调用一次构造函数:

CodeBuilder code{"Dog"};
code.add_field("name", "std::string").add_field("legs", "int");

该写法无任何临时对象生成,自然没有拷贝、移动开销,运行逻辑完全合法。

方案2:添加移动构造+显式std::move调用(保留链式初始化写法)

如果你需要保留一行完成链式初始化的写法,首先给CodeBuilder和MyString补充移动构造函数,再在初始化时显式将返回的左值转为右值,触发移动语义:

补充移动构造代码:

class MyString
{
    std::string str;
public:
    // 原有实现保持不变,新增移动构造
    MyString(MyString&& other) noexcept : str(std::move(other.str)) {
        // 仅调试用,业务代码可删除打印
        std::cout << "MyString Move Ctor" << std::endl;
    }
};

class CodeBuilder
{
    MyString _code;
public:
    CodeBuilder(const std::string& class_name) { /*原有实现*/ }
    // 新增移动构造
    CodeBuilder(CodeBuilder&& other) noexcept : _code(std::move(other._code)) {
        // 仅调试用,业务代码可删除打印
        std::cout << "CodeBuilder Move Ctor" << std::endl;
    }

    CodeBuilder& add_field(const std::string& name, const std::string& type)
    {
        /*原有实现*/
        return *this;
    }
};

调整初始化写法:

CodeBuilder code = std::move(CodeBuilder{"Dog"}.add_field("name", "std::string").add_field("legs", "int"));

该方案仅会产生一次默认构造+一次移动构造,移动操作的开销极低。如果你使用C++17及以上版本,编译器还会触发强制复制消除,连移动构造都会被省略,直接将临时对象构造到code的内存空间。

方案3:重载右值限定的add_field(原有调用写法无需修改)

如果你不想调整业务侧的调用代码,可以给add_field增加引用限定符重载,右值对象调用时自动返回右值引用,自动触发移动/复制消除:

class CodeBuilder
{
    MyString _code;
public:
    CodeBuilder(const std::string& class_name) { /*原有实现*/ }
    CodeBuilder(CodeBuilder&& other) noexcept : _code(std::move(other._code)) {}

    // 左值对象调用时返回左值引用
    CodeBuilder& add_field(const std::string& name, const std::string& type) &
    {
        /*原有实现*/
        return *this;
    }

    // 右值对象调用时返回右值引用
    CodeBuilder&& add_field(const std::string& name, const std::string& type) &&
    {
        /*原有实现*/
        return std::move(*this);
    }
};

修改后你原有调用代码不需要做任何调整,编译器会自动识别链式调用的是右值对象,最终返回右值引用触发移动构造或C++17强制复制消除,全程无多余拷贝开销。


内容的提问来源于stack exchange,提问作者DeSpeaker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 08:06:03