为含AVL树嵌套结构的Team类编写拷贝构造等函数遇段错误
问题描述
我有一个Team类,内部包含两棵AVL树:一棵存储指向Player对象的shared_ptr,另一棵存储指向PlayerKeyId对象的shared_ptr。PlayerKeyId是Player的子类,仅重载了<和>运算符,用于按不同维度组织球队球员。
最初使用默认构造、拷贝构造和operator=时出现大量内存泄漏,自行编写这些函数后又出现段错误。相关代码及修复方案如下:
核心问题与修复要点
1. Node类的拷贝/赋值逻辑错误
- 原拷贝构造中
data = new T();属于冗余操作:T是shared_ptr<Player>这类值类型,直接data = oldNode.data即可完成安全拷贝。 - 原拷贝构造仅浅拷贝子节点指针,会导致多个
Node共享同一子树,析构时重复释放内存引发段错误,需改为递归深拷贝。 - 原赋值运算符直接赋值指针属于浅拷贝,需补充内存释放与深拷贝逻辑,同时增加自赋值判断避免崩溃。
- 析构函数无需手动置空
parent,递归释放左右子节点即可。
2. AVLTree类的拷贝/赋值逻辑错误
- 原拷贝构造仅浅拷贝根节点指针,需调用
Node的拷贝构造完成整棵树的深拷贝。 - 原赋值运算符直接共享根节点,需先释放旧树内存,再深拷贝新树。
3. Player类的拷贝/赋值问题
- 默认构造未初始化
followingPlayer和previousPlayer,导致野指针操作崩溃,需初始化为nullptr。 - 拷贝构造需判断源指针是否为空,再深拷贝链表节点,避免空指针解引用。
- 赋值运算符需先释放旧节点内存,再深拷贝新节点,同时增加自赋值判断。
4. Team类的拷贝/赋值问题
- 拷贝构造与赋值运算符需先释放旧AVL树内存,再深拷贝新树,避免内存泄漏与重复释放。
- 无需手动置空
topScorer,智能指针会自动管理引用计数。
5. 简化建议
优先遵循Rule of Zero原则,用shared_ptr<Node<T>>管理AVL节点,避免手动编写拷贝/析构逻辑,减少内存错误。若必须手动管理内存,严格遵循Rule of Three:写了析构函数就必须配套正确的拷贝构造与赋值运算符。
修复后代码示例
Player类
class Player{ public: int playerID; int goals; int cards; int playerGamesPlayed; bool goalkeeper; Node<shared_ptr<Player>>* followingPlayer; Node<shared_ptr<Player>>* previousPlayer; shared_ptr<Team> playerTeamPointer; int teamPlayerId; Player(int playerID,int teamID, int gamesPlayed, int goals,int cards,bool goalkeeper, shared_ptr<Team> playerTeamPointer) :playerID(playerID),goals(goals),cards(cards),playerGamesPlayed(gamesPlayed),goalkeeper(goalkeeper),playerTeamPointer(playerTeamPointer),teamPlayerId(teamID), followingPlayer(nullptr), previousPlayer(nullptr) {}; ~Player(){ delete followingPlayer; delete previousPlayer; }; Player(const Player& p){ playerID = p.playerID; goals = p.goals; cards = p.cards; playerGamesPlayed = p.playerGamesPlayed; goalkeeper = p.goalkeeper; teamPlayerId = p.teamPlayerId; playerTeamPointer = p.playerTeamPointer; followingPlayer = p.followingPlayer ? new Node<shared_ptr<Player>>(*p.followingPlayer) : nullptr; previousPlayer = p.previousPlayer ? new Node<shared_ptr<Player>>(*p.previousPlayer) : nullptr; } Player& operator=(const Player& p){ if(this == &p) return *this; delete followingPlayer; delete previousPlayer; playerID = p.playerID; goals = p.goals; cards = p.cards; playerGamesPlayed = p.playerGamesPlayed; goalkeeper = p.goalkeeper; teamPlayerId = p.teamPlayerId; playerTeamPointer = p.playerTeamPointer; followingPlayer = p.followingPlayer ? new Node<shared_ptr<Player>>(*p.followingPlayer) : nullptr; previousPlayer = p.previousPlayer ? new Node<shared_ptr<Player>>(*p.previousPlayer) : nullptr; return *this; } Player() : followingPlayer(nullptr), previousPlayer(nullptr) {} };
Team类
class Team{ public: int teamID; int points; int playerCount; int teamGames; shared_ptr<Player> topScorer; int totalGoals; int totalCards; int goalKeepers; bool valid; AVLTree<shared_ptr<PlayerKeyId>> *playersId; AVLTree<shared_ptr<Player>> *playersGoals; Team(int teamID, int points) :teamID(teamID),points(points),playerCount(0),teamGames(0), totalGoals(0),totalCards(0),goalKeepers(0),valid(false) { topScorer = make_shared<PlayerKeyId>(-1,-1,-1,-1,-1,false,nullptr); playersId = new AVLTree<shared_ptr<PlayerKeyId>>(); playersGoals = new AVLTree<shared_ptr<Player>>(); } Team(const Team& t){ teamID = t.teamID; points = t.points; playerCount = t.playerCount; teamGames = t.teamGames; topScorer = t.topScorer; totalGoals = t.totalGoals; totalCards = t.totalCards; goalKeepers = t.goalKeepers; valid = t.valid; playersId = new AVLTree<shared_ptr<PlayerKeyId>>(*t.playersId); playersGoals = new AVLTree<shared_ptr<Player>>(*t.playersGoals); } Team& operator=(const Team& t){ if(this == &t) return *this; delete playersId; delete playersGoals; teamID = t.teamID; points = t.points; playerCount = t.playerCount; teamGames = t.teamGames; topScorer = t.topScorer; totalGoals = t.totalGoals; totalCards = t.totalCards; goalKeepers = t.goalKeepers; valid = t.valid; playersId = new AVLTree<shared_ptr<PlayerKeyId>>(*t.playersId); playersGoals = new AVLTree<shared_ptr<Player>>(*t.playersGoals); return *this; } ~Team(){ delete playersId; delete playersGoals; } };
PlayerKeyId类
class PlayerKeyId:public Player{ public: using Player::Player; bool operator<(const PlayerKeyId& other) const { return playerID < other.playerID; } bool operator>(const PlayerKeyId& other) const { return playerID > other.playerID; } };
AVL树相关类
template <class T> class Node { public: Node* left; T data; Node* right; int height; Node* parent; Node() : left(nullptr), right(nullptr), height(0), parent(nullptr) {} Node(const Node& oldNode) : height(oldNode.height), parent(nullptr) { data = oldNode.data; left = oldNode.left ? new Node(*oldNode.left) : nullptr; if(left) left->parent = this; right = oldNode.right ? new Node(*oldNode.right) : nullptr; if(right) right->parent = this; } Node& operator=(const Node& oldNode){ if(this == &oldNode) return *this; delete left; delete right; data = oldNode.data; height = oldNode.height; parent = nullptr; left = oldNode.left ? new Node(*oldNode.left) : nullptr; if(left) left->parent = this; right = oldNode.right ? new Node(*oldNode.right) : nullptr; if(right) right->parent = this; return *this; } ~Node(){ delete left; delete right; } }; template <class T> class AVLTree{ public: Node<T>* root; AVLTree() : root(nullptr) {} AVLTree(const AVLTree& oldAVL) { root = oldAVL.root ? new Node<T>(*oldAVL.root) : nullptr; } AVLTree& operator=(const AVLTree& oldAVL){ if(this == &oldAVL) return *this; delete root; root = oldAVL.root ? new Node<T>(*oldAVL.root) : nullptr; return *this; } ~AVLTree(){ delete root; } };
内容的提问来源于stack exchange,提问作者Algo
相关产品推荐
相关产品推荐

