C++中含shared_ptr的vector赋值运算符重载问题:拷贝游戏对象后打印副本崩溃排查
你遇到的崩溃大概率是因为缺少拷贝构造函数的实现,再结合其他潜在的代码问题共同导致的,咱们一步步拆解:
核心问题:拷贝初始化调用的不是赋值运算符
你主函数里的Game g2 = g1;是拷贝初始化操作,会调用Game的拷贝构造函数,而不是你重载的operator=。编译器自动生成的默认拷贝构造函数只会做逐成员浅拷贝:
vector<Pair>的拷贝是深拷贝,但Pair里的std::shared_ptr<Character>只会增加引用计数——这意味着g1和g2会共享同一个Character实例,完全不符合你想要的"副本与原对象独立"的深拷贝需求。- 如果
Character的析构函数不是虚函数,或者Soldier的内部有未正确拷贝的动态资源,这种共享很容易引发内存访问错误,直接导致崩溃。
解决方案:实现拷贝构造函数
把你赋值运算符里的深拷贝逻辑复用过来,实现拷贝构造函数:
Game::Game(const Game& other) { std::vector<Pair> new_grid; for(int i=0; i < other.grid_characters.size(); i++){ Character* character = other.grid_characters[i].character->clone(); std::shared_ptr<Character> character_shared_ptr(character); Pair new_element( other.grid_characters[i].grid_point, character_shared_ptr); new_grid.push_back(new_element); } this->height = other.height; this->width = other.width; this->grid_characters = new_grid; }
优化建议:使用拷贝交换Idiom
为了避免代码重复,同时自动处理自赋值问题,推荐用拷贝交换Idiom重构赋值运算符:
// 先实现一个swap成员函数 void Game::swap(Game& other) noexcept { std::swap(height, other.height); std::swap(width, other.width); std::swap(grid_characters, other.grid_characters); } // 赋值运算符通过传值调用自动触发拷贝构造,再交换数据 Game& Game::operator=(Game other) { swap(other); return *this; }
其他潜在崩溃点检查
1. Character类必须有虚析构函数
如果Character的析构函数不是虚函数,std::shared_ptr<Character>销毁时只会调用基类析构函数,不会触发Soldier的析构逻辑,会导致Soldier的动态成员内存泄漏,甚至引发未定义行为。确保基类析构函数是虚函数:
class Character { public: virtual ~Character() = default; // 必须是虚析构 virtual Character* clone() const = 0; // ...其他成员 };
2. 检查getCharacterAtPoint函数的逻辑
你没给出这个函数的实现,但如果它遍历grid_characters时用了错误的循环条件(比如i <= game.grid_characters.size()),会直接导致数组越界访问,触发崩溃。确保循环只访问有效索引:i < game.grid_characters.size()。
3. 给Pair结构体添加默认构造函数
你的Pair只有带参数的构造函数,虽然当前代码不会触发问题,但后续如果对grid_characters做resize或无参数emplace_back操作,会编译失败。建议添加默认构造函数:
struct Pair { GridPoint grid_point; std::shared_ptr<Character> character; Pair() = default; // 添加默认构造 Pair(GridPoint grid_point, std::shared_ptr<Character> character) : grid_point(grid_point), character(character) {} };
总结
先实现拷贝构造函数,再用拷贝交换Idiom优化赋值运算符,同时确保Character有虚析构函数,检查getCharacterAtPoint的循环逻辑,这些步骤应该能解决你的崩溃问题,同时实现正确的深拷贝。
内容的提问来源于stack exchange,提问作者SpaceNugget

