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

如何简化含裸指针类的移动构造函数与赋值运算符编写?

不放弃裸指针的前提下简化移动操作代码

完全可以做到,核心思路要么是把裸指针的所有权逻辑封装成小型RAII类,让编译器自动处理其他成员的移动;要么是用std::tie批量处理成员移动,避免重复写std::move。

推荐方案:封装裸指针为RAII类

这是最简洁且安全的方式,既保留了对裸指针的访问权,又把繁琐的所有权管理逻辑剥离出来,主类可以直接用编译器默认生成的移动操作:

// 封装设备指针的RAII类
class MultiplexDeviceHolder {
public:
    MultiplexDeviceHolder() 
        : m_ptr(getDevice(B737M_mpDevice, true))
    {
        engageDevice(m_ptr);
    }

    // 手动实现移动构造/赋值,处理指针所有权转移
    MultiplexDeviceHolder(MultiplexDeviceHolder&& other) noexcept
        : m_ptr(other.m_ptr)
    {
        other.m_ptr = nullptr;
    }

    MultiplexDeviceHolder& operator=(MultiplexDeviceHolder&& other) noexcept {
        if (this != &other) {
            // 清理当前资源
            if (m_ptr) {
                disengageDevice(m_ptr);
                delete m_ptr;
            }
            // 转移所有权
            m_ptr = other.m_ptr;
            other.m_ptr = nullptr;
        }
        return *this;
    }

    // 自动清理资源
    ~MultiplexDeviceHolder() {
        if (m_ptr) {
            disengageDevice(m_ptr);
            delete m_ptr;
        }
    }

    // 提供裸指针访问接口,满足需求
    char STRONG* get() const noexcept { return m_ptr; }

    // 禁用拷贝
    MultiplexDeviceHolder(const MultiplexDeviceHolder&) = delete;
    MultiplexDeviceHolder& operator=(const MultiplexDeviceHolder&) = delete;

private:
    char STRONG* m_ptr = nullptr;
};

// 修改后的Example类
class Example {
public:
    Example() = default; // 设备初始化由RAII类自动处理

    // 编译器自动生成默认移动构造/赋值,无需手动写任何成员移动代码
    Example(Example&&) = default;
    Example& operator=(Example&&) = default;

    // 禁用拷贝
    Example(const Example&) = delete;
    Example& operator=(const Example&) = delete;

    // 如需访问设备指针,调用get()即可
    char STRONG* getMultiplexDevice() const noexcept { return m_deviceHolder.get(); }

private:
    MultiplexDeviceHolder m_deviceHolder;
    std::vector<uint32> m_pilotStack;
    std::vector<uint32> m_pasStack;
    std::vector<uint32> m_pitchAttackLimits;
    std::vector<uint32> m_yawToPitchBalance;
    std::vector<uint32> m_engineBalance;
    // ... 其他成员
};

备选方案:用std::tie批量处理成员移动

如果不想封装RAII类,也可以用std::tie把多个成员的移动操作合并成一行,减少复制粘贴:

class Example {
public:
    Example() 
        : m_multiplexDevice(getDevice(B737M_mpDevice, true))
    {
        engageDevice(m_multiplexDevice);
    }

    Example(Example&& other) noexcept
        : m_multiplexDevice(other.m_multiplexDevice) // 先转移指针所有权
    {
        // 用std::tie批量移动所有容器成员,避免逐个写std::move
        std::tie(m_pilotStack, m_pasStack, m_pitchAttackLimits, m_yawToPitchBalance, m_engineBalance) =
            std::move(std::tie(other.m_pilotStack, other.m_pasStack, other.m_pitchAttackLimits, other.m_yawToPitchBalance, other.m_engineBalance));
        
        other.m_multiplexDevice = nullptr; // 置空源对象指针
    }

    Example& operator=(Example&& other) noexcept {
        if (this != &other) {
            // 先清理当前对象的设备资源(建议补上,避免内存泄漏)
            if (m_multiplexDevice) {
                disengageDevice(m_multiplexDevice);
                delete m_multiplexDevice;
            }

            // 批量移动容器成员
            std::tie(m_pilotStack, m_pasStack, m_pitchAttackLimits, m_yawToPitchBalance, m_engineBalance) =
                std::move(std::tie(other.m_pilotStack, other.m_pasStack, other.m_pitchAttackLimits, other.m_yawToPitchBalance, other.m_engineBalance));
            
            // 转移指针所有权并置空源指针
            m_multiplexDevice = other.m_multiplexDevice;
            other.m_multiplexDevice = nullptr;
        }
        return *this;
    }

    Example(const Example&) = delete;
    Example& operator=(const Example&) = delete;

    ~Example() {
        if (m_multiplexDevice)
            disengageDevice(m_multiplexDevice);
        delete m_multiplexDevice;
    }

private:
    char STRONG * m_multiplexDevice;
    std::vector<uint32> m_pilotStack;
    std::vector<uint32> m_pasStack;
    std::vector<uint32> m_pitchAttackLimits;
    std::vector<uint32> m_yawToPitchBalance;
    std::vector<uint32> m_engineBalance;
    // ... 其他成员
};

关于“先执行默认移动再置空指针”的疑问

你提到的先执行默认生成的移动操作再置空指针的思路,因为C++中无法重载移动构造函数(签名固定),所以无法直接实现。不过上面的RAII方案本质上已经达到了类似效果:默认移动操作处理所有成员(包括RAII类的移动),而RAII类内部已经完成了指针置空的逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 09:33:20