C++中map内Block对象setRoot方法调用异常问题排查
问题分析与修复
你的核心问题在于操作的对象和map中存储的对象不是同一个实例,导致修改无法同步,具体有以下几个关键点:
1. map存储的是对象副本,而非原对象
在main函数中,你先创建了栈上的block1/block2/block3,然后将它们复制到map中:
map<int, Block<int>> blocks = {{1, block1}, {2, block2}, {3, block3}};
这意味着栈上的block3和map中键3对应的Block<int>是完全独立的两个对象。你在unionto中修改的是map里的对象,但最后输出的是栈上block3的toroot,自然看不到变化。
2. unionto参数传值导致操作副本
unionto的参数Block other是传值方式,会创建传入对象的副本:
void Block<T>::unionto(Block other, map<T, Block<T>> &blocks, int reverse, int flank)
调用other.find(blocks)时,操作的是副本的toroot,而非map中对应对象的toroot,这会导致find的路径压缩逻辑完全失效,无法正确找到根节点。
3. find函数修改的是副本的属性
当你调用栈上block3的find方法时,this指向的是栈上的block3,而非map中的对象。this->toroot = rootid;修改的是栈上对象的属性,对map里的实例没有任何影响。
修复方案
方案一:让map存储智能指针(推荐)
C++容器无法直接存储引用,所以用智能指针来管理对象,确保所有操作的是同一个实例:
修改Block.h的方法签名
调整unionto和find的参数,适配指针存储的map:
template <class T> class Block { // ... 其他成员不变 T find(map<T, unique_ptr<Block<T>>>& blocks); void unionto(Block& other, map<T, unique_ptr<Block<T>>>& blocks, int reverse, int flank); // ... };
修改Block.cpp的实现
template <class T> T Block<T>::find(map<T, unique_ptr<Block<T>>>& blocks){ T rootid = this->id; if(this->toroot != rootid){ // 从map中获取根节点的实例,递归find rootid = blocks[this->toroot]->find(blocks); this->toroot = rootid; } return rootid; } template <class T> void Block<T>::unionto(Block& other, map<T, unique_ptr<Block<T>>>& blocks, int reverse, int flank){ T selfroot = this->find(blocks); T otheroot = other.find(blocks); // 修改map中selfroot对应对象的toroot blocks[selfroot]->setRoot(otheroot); }
修改main.cpp
int main(){ // 用unique_ptr存储Block实例到map map<int, unique_ptr<Block<int>>> blocks; blocks[1] = make_unique<Block<int>>(1, "actg"); blocks[2] = make_unique<Block<int>>(2, "actg"); blocks[3] = make_unique<Block<int>>(3, "actg"); // 调用unionto时传入map中对象的引用 blocks[3]->unionto(*blocks[2], blocks, 1, 1); // 输出map中block3的toroot cout << blocks[3]->toroot; // 现在会输出2,符合预期 }
方案二:直接操作map中的对象(避免栈上副本)
如果不想用指针,也可以全程只操作map中的对象,不创建栈上的副本:
int main(){ map<int, Block<int>> blocks; // 直接在map中构造对象 blocks.emplace(1, 1, "actg"); blocks.emplace(2, 2, "actg"); blocks.emplace(3, 3, "actg"); // 调用map中block3的unionto方法,传入map中block2的引用 blocks[3].unionto(blocks[2], blocks, 1, 1); // 输出map中block3的toroot cout << blocks[3].toroot; // 输出2 }
同时要将unionto的参数改成引用:
void unionto(Block& other, map<T, Block<T>>& blocks, int reverse, int flank);
额外注意事项
模板类的实现通常需要放在头文件中,或者在cpp文件末尾显式实例化(比如template class Block<int>;),否则会出现链接错误。
内容的提问来源于stack exchange,提问作者Martin Inf1n1ty
相关产品推荐
相关产品推荐

