C++双指针场景下如何实现Car类Wheel数据不受原数组修改影响
问题根因
你遇到的核心矛盾是双指针的两层内存所有权没有和Car实例绑定:
- 浅拷贝指针数组只复制了地址,所有修改会同步影响Car和Truck的数据
- 临时数组存深拷贝对象的写法,因为申请的连续Wheel数组没有纳入Car的生命周期管理,才会出现「释放就野指针、不释放就泄漏」的问题
Truck不需要持有数据所有权,仅做浅拷贝即可同步感知原对象修改;Car需要持有深拷贝后的Wheel对象所有权,保证数据独立。
基于现有双指针成员的正确实现
你不需要额外声明temp数组存储拷贝对象,直接在构造时完成两层内存申请,同时给Car实现匹配的析构函数释放内存即可,注意要禁用默认的拷贝构造、拷贝赋值,避免默认浅拷贝导致重复释放内存。
修正后的Car类定义
#include <cstddef> #include <string> // Wheel类保持原有定义即可 class Wheel { unsigned m_orderID{}; std::string m_name{}; }; class Car{ private: const Wheel** m_ptrToArray{}; size_t m_count{}; public: Car(const Wheel* wheels[], size_t count); ~Car(); // 禁用默认拷贝逻辑,避免浅拷贝导致double free Car(const Car&) = delete; Car& operator=(const Car&) = delete; // 可选:访问接口 const Wheel* getWheel(size_t idx) const { return idx < m_count ? m_ptrToArray[idx] : nullptr; } size_t wheelCount() const { return m_count; } };
构造函数深拷贝实现
Car::Car(const Wheel* wheels[], size_t count) : m_count(count) { // 第一层:申请存储Wheel指针的数组内存 m_ptrToArray = new const Wheel*[m_count]; // 第二层:为每个Wheel单独申请内存,调用拷贝构造生成独立副本 for (size_t i = 0; i < m_count; i++) { m_ptrToArray[i] = new Wheel(*wheels[i]); } }
析构函数内存释放实现
内存申请和释放严格配对:逐个释放每个独立拷贝的Wheel对象,再释放存储指针的数组本身,不会出现内存泄漏或野指针。
Car::~Car() { if (!m_ptrToArray) return; // 先释放每个Wheel副本 for (size_t i = 0; i < m_count; i++) { delete m_ptrToArray[i]; } // 再释放指针数组 delete[] m_ptrToArray; m_ptrToArray = nullptr; m_count = 0; }
Truck类实现(同步感知修改)
Truck不需要做深拷贝,直接浅拷贝传入的指针即可,注意Truck不持有传入指针的所有权,不需要在析构中释放这部分内存:
class Truck{ private: const Wheel** m_ptrToArray{}; size_t m_count{}; public: Truck(const Wheel* wheels[], size_t count) : m_count(count) { // 如果需要存储独立的指针数组而非直接用传入参数,只拷贝第一层指针即可 m_ptrToArray = new const Wheel*[count]; for (size_t i=0; i<count; i++) m_ptrToArray[i] = wheels[i]; } ~Truck() { // 仅释放自己申请的指针数组,不要释放指向的Wheel原对象 if (m_ptrToArray) delete[] m_ptrToArray; } const Wheel* getWheel(size_t idx) const { return idx < m_count ? m_ptrToArray[idx] : nullptr; } size_t wheelCount() const { return m_count; } };
更推荐的现代C++简化方案
如果不强制要求使用双指针成员,完全可以用标准库容器自动管理内存,不需要手动写new/delete,从根源避免内存问题:
#include <vector> class Car{ private: // 直接存储Wheel值对象,vector自动管理内存,深拷贝逻辑自动生成 std::vector<Wheel> m_ownedWheels; public: Car(const Wheel* wheels[], size_t count) { m_ownedWheels.reserve(count); for (size_t i = 0; i < count; i++) { m_ownedWheels.emplace_back(*wheels[i]); } } // 不需要自定义析构、拷贝、移动逻辑,编译器自动生成的实现完全正确 const Wheel& getWheel(size_t idx) const { return m_ownedWheels[idx]; } size_t wheelCount() const { return m_ownedWheels.size(); } }; class Truck{ private: // 存储原对象的指针,不持有所有权 std::vector<const Wheel*> m_refWheels; public: Truck(const Wheel* wheels[], size_t count) { m_refWheels.reserve(count); for (size_t i = 0; i < count; i++) { m_refWheels.push_back(wheels[i]); } } const Wheel& getWheel(size_t idx) const { return *m_refWheels[idx]; } size_t wheelCount() const { return m_refWheels.size(); } };
之前写法的问题说明
你之前用new Wheel[m_count]申请连续数组的写法存在本质缺陷:C++中通过new[]申请的连续数组,必须通过delete[]一次性释放整个数组,不能对数组中单个元素的地址调用delete。如果要保留这种连续存储的方式,你需要额外在Car类中保存连续数组的首地址,析构时直接delete[]释放整个数组,不需要逐个释放元素,但这种写法需要额外维护一个指针成员,不如逐个new单个对象的实现直观。
内容的提问来源于stack exchange,提问作者OldSchool Ty
相关产品推荐
相关产品推荐

