C++列表循环删除成员及for循环、erase/delete使用问题咨询
问题1:为何程序员省略了for循环的第三个参数?是否隐含了i++操作?
咱们先回忆下C++里for循环的基本结构:for(初始化表达式; 条件表达式; 增量表达式),其中增量表达式是可选的——不是必须写在第三个位置里的。
在这段代码里,程序员之所以省略第三个参数,是因为迭代器的增量逻辑不是固定的i++:
- 当
e->boolean为true时,我们需要正常往后移动迭代器,所以执行i++; - 当
e->boolean为false时,我们调用了list::erase(i),这个函数会移除当前迭代器指向的元素,并且返回下一个有效的迭代器,此时我们直接把这个返回值赋值给i,不需要再执行i++(否则会跳过元素甚至导致迭代器失效)。
这里完全没有隐含的i++操作,所有的迭代器移动逻辑都被显式写在了循环体里,这样才能根据不同的条件正确处理迭代器的状态。
问题2:erase、delete的使用场景,代码中为何必须并用;以及为何要先存Base *e = *i
关于erase和delete的区别与使用时机
erase是容器(这里是std::list)的成员函数,它的作用是把元素从容器中移除,但不会处理元素本身的内存。比如这段代码里,erase只会把Base*指针从list的节点中拿掉,不会释放指针指向的堆内存。delete是C++的内存释放运算符,它的作用是释放用new分配的堆内存,但不会把指针从容器中移除。如果只调用delete不调用erase,list里会留下“悬空指针”——指向已经被释放的内存的指针,后续遍历容器时访问这些指针会导致未定义行为。- 两者并用的场景:当容器存储的是指向堆对象的指针时,如果你既要把指针从容器中移除,又要释放指针指向的对象内存,就需要同时使用
erase和delete。
回到这段代码,必须这么做的原因:
这些Base对象是用new在堆上创建的,如果不调用delete,会造成内存泄漏;如果只delete不erase,list里的悬空指针会成为隐患。所以代码里先通过erase把指针从list中移除(同时拿到下一个有效迭代器),再用delete释放对象内存,既清理了容器,又避免了内存泄漏。
为什么要写Base *e = *i;而非直接i->foo(); i->goo();
这里要搞清楚迭代器的类型:i是std::list<Base*>::iterator,也就是指向list中元素的迭代器,而list中的元素是Base*类型的指针。
- 如果你直接写
i->foo(),相当于调用迭代器的operator->,它会返回当前元素的地址——也就是Base**(指向指针的指针),而Base**并没有foo()成员函数,编译直接会报错。 - 正确的做法是先解引用迭代器拿到
Base*指针:*i就是我们要的Base*,把它赋值给e之后,用e->foo()就能正常调用Base类的成员函数了。另外,后面我们还要用这个指针执行delete操作,提前把它存到变量e里,代码会更清晰,也避免了重复解引用迭代器的操作。
内容的提问来源于stack exchange,提问作者Powereleven
相关产品推荐
相关产品推荐

