C++中使用迭代器修改对象时遇到的问题
嘿,我仔细看了你的代码,问题的根源其实是对象切片加上容器存储方式不对,导致你调用push/pop时根本没操作到真正的Stack/Queue对象,而且你的SimpleList里还有几个逻辑bug,咱们一步步来理清楚:
核心问题:对象切片与多态失效
你定义的三个列表liI、liD、liS的类型是std::list<SimpleList<std::string>>,这意味着它们存储的是SimpleList基类的值对象。当你在Create函数里创建Stack或Queue对象,然后push_back到列表中时,会发生对象切片——派生类的特有部分会被砍掉,只保留基类的内容。这就导致:
- 后续通过迭代器访问的都是
SimpleList基类对象,调用push时执行的是基类里的空实现,而不是Stack/Queue中重写的版本; - 你以为在修改原来的
Stack/Queue,实际上操作的是被切片后的基类副本,自然看不到变化。
另外,你的IsIn函数传参是std::list<SimpleList<std::string>> li,这会拷贝整个列表,不仅效率低,还可能导致判断逻辑出现问题,应该传引用。
还有SimpleList的几个成员函数逻辑有问题:
addEnd里的if(size > 0) {(*end).next = temp;}是错误的,这会让旧的end的next指向自己,形成循环链表,导致后续remove操作出错;remove函数返回的是&((*start).data),当start被移动后,这个指针会变成悬空指针,非常危险,应该直接返回值而不是指针。
修复方案
1. 改用智能指针存储多态对象
把列表的类型改成存储std::unique_ptr<SimpleList<std::string>>,这样可以安全地存储派生类对象,实现多态,同时避免内存泄漏。
2. 修正Create函数
用std::make_unique创建Stack/Queue对象,然后push_back到列表中,避免对象切片。
3. 修正所有涉及列表访问的函数
因为现在存储的是指针,所以访问成员时要改用->而不是.,同时修改IsIn、Push、Pop等函数的逻辑适配指针。
4. 修复SimpleList的成员函数bug
修正addEnd和remove的逻辑错误。
修改后的完整代码
#include <iostream> #include <fstream> #include <vector> #include <sstream> #include <string> #include <list> #include <memory> #include <stdexcept> using namespace std; template <class T> class SimpleList { struct Node { T data; Node* next; Node(T datI, Node* nextI) : data(datI), next(nextI) {} }; protected: Node* start; int size; Node* end; std::string name; public: SimpleList(std::string title) : name(title), size(0), start(nullptr), end(nullptr) {} virtual ~SimpleList() { Node* current = start; while (current != nullptr) { Node* next = current->next; delete current; current = next; } } void addFront(T element) { Node *temp = new Node(element, start); if (size == 0) { end = temp; } start = temp; size++; } void addEnd(T element) { Node *temp = new Node(element, nullptr); if (size == 0) { start = temp; end = temp; } else { end->next = temp; end = temp; } size++; } T remove() { if (size == 0) { throw std::runtime_error("List is empty"); } Node* tempNode = start; T data = tempNode->data; start = start->next; delete tempNode; size--; if (size == 0) { end = nullptr; } return data; } virtual void push(T element) = 0; virtual T pop() { return remove(); } }; template <class T> class Stack : public SimpleList<T> { public: Stack(std::string title) : SimpleList<T>(title) {} void push(T element) override { this->addFront(element); } }; template <class T> class Queue : public SimpleList<T> { public: Queue(std::string title) : SimpleList<T>(title) {} void push(T element) override { this->addEnd(element); } }; std::list<std::unique_ptr<SimpleList<std::string>>> liI; std::list<std::unique_ptr<SimpleList<std::string>>> liD; std::list<std::unique_ptr<SimpleList<std::string>>> liS; bool IsIn(std::string title, const std::list<std::unique_ptr<SimpleList<std::string>>>& li) { for (const auto& ptr : li) { if (ptr->name == title) { return true; } } return false; } std::string Pop(std::vector<std::string> para, std::list<std::unique_ptr<SimpleList<std::string>>>* li) { if (!IsIn(para[1], *li)) { return "\nERROR: This name does not exist!\n"; } for (auto& ptr : *li) { if (ptr->name == para[1]) { try { return "\nValue popped: " + ptr->pop() + "\n"; } catch (const std::runtime_error& e) { return "\nERROR: " + std::string(e.what()) + "\n"; } } } return ""; } std::string Push(std::vector<std::string> para, std::list<std::unique_ptr<SimpleList<std::string>>>* li) { if (!IsIn(para[1], *li)) { return "\nERROR: This name does not exist!\n"; } for (auto& ptr : *li) { if (ptr->name == para[1]) { ptr->push(para[2]); return "\n"; } } return ""; } std::string Create(std::vector<std::string> para, std::list<std::unique_ptr<SimpleList<std::string>>>* li) { if (IsIn(para[1], *li)) { return "\nERROR: This name already exists!\n"; } if (para[2] == "stack") { li->push_back(std::make_unique<Stack<std::string>>(para[1])); } else { li->push_back(std::make_unique<Queue<std::string>>(para[1])); } return "\n"; } std::string Processing(std::vector<std::string> para) { std::string message; if (para[0] == "create") { if (para[1][0] == 'i') { message = Create(para, &liI); } else if (para[1][0] == 's') { message = Create(para, &liS); } else { message = Create(para, &liD); } } else if (para[0] == "push") { if (para[1][0] == 'i') { message = Push(para, &liI); } else if (para[1][0] == 's') { message = Push(para, &liS); } else { message = Push(para, &liD); } } else if (para[0] == "pop") { if (para[1][0] == 'i') { message = Pop(para, &liI); } else if (para[1][0] == 's') { message = Pop(para, &liS); } else { message = Pop(para, &liD); } } return message; } int main() { std::vector<std::string> vec; vec.push_back("create"); vec.push_back("i1"); vec.push_back("queue"); std::cout << Processing(vec); vec.clear(); vec.push_back("push"); vec.push_back("i1"); vec.push_back("500"); std::cout << Processing(vec); vec.clear(); vec.push_back("pop"); vec.push_back("i1"); std::cout << Processing(vec); vec.clear(); vec.push_back("pop"); vec.push_back("i1"); std::cout << Processing(vec); return 0; }
关键修改点说明
- 智能指针存储:用
std::unique_ptr存储SimpleList的派生类对象,完美解决对象切片问题,保证多态生效,同时自动管理内存避免泄漏。 - 纯虚函数与虚析构:把
push改成纯虚函数,强制派生类必须实现;添加虚析构函数,确保派生类对象能被正确析构。 - 修正
SimpleList的bug:addEnd函数修复了循环链表的问题,正确维护链表的尾节点;remove函数改为返回值,避免悬空指针,同时添加异常处理空列表的情况。
- 传参优化:
IsIn函数改为传列表的const引用,避免不必要的拷贝,提升效率。 - 逻辑修正:
Processing函数里的条件判断改成else if,避免多个if同时触发的逻辑错误。
现在运行修改后的代码,你会发现push和pop操作都能正确作用到对应的Queue/Stack对象上了,输出应该是:
Value popped: 500 ERROR: List is empty!
内容的提问来源于stack exchange,提问作者snowball

