如何简化含裸指针类的移动构造函数与赋值运算符编写?
不放弃裸指针的前提下简化移动操作代码
完全可以做到,核心思路要么是把裸指针的所有权逻辑封装成小型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
相关产品推荐
相关产品推荐

