在范围 (C++) 之外列出插入迭代器

List insert iterator outside range (C++)

本文关键字:插入 迭代器 范围 C++      更新时间:2023-10-16

>我有一个可以响应doSomething()的类A和一些子类BCD,它们都覆盖了doSomething()B特别有一个实例变量,它是标准库中的列表,并在响应doSomething()时将元素添加到此类列表中。

在我的代码的某些部分,我创建了一个数组,其中包含指向 A 类型的对象的指针。现在,如果我这样声明:

A* pointersToA[3];
pointersToA[0] = &B();
pointersToA[1] = &C();
pointersToA[2] = &D();

打电话给pointersToA[0]->doSomething()时我得到了一个List insert iterator outside range.

但是如果我这样做:

A* pointersToA[3];
B b = B();
pointersToA[0] = &b;
pointersToA[1] = &C();
pointersToA[2] = &D();

一切都按预期工作。

为什么会这样?

获取右值的地址几乎可以保证迟早(通常更早)会导致问题,并且实际上在C++中是非法的(因此您的编译器在这方面并不完全是一流的)。地址不是对对象的引用;由于C++不是垃圾回收语言,因此一旦执行了BCD的值,它们就会消失。

对于您的使用,动态分配听起来是最直接的方式:

std::array<std::unique_ptr<A>, 3> pointersToA {
    std::make_unique<B>(),
    std::make_unique<C>(),
    std::make_unique<D>()
};

当你这样做时:

pointersToA[0] = &B();

您正在pointersToA[0]中存储指向临时的指针。这在标准C++中无效,符合要求的实现应发出错误。这个编译器的事实表明你的编译器有一个错误或非标准扩展。

假设编译器接受该代码,则问题在于,在该行之后,pointersToA[0]将失效,因为它指向的B对象不再存在。取消引用该指针是未定义的行为

当你这样做时

B b = B();
pointersToA[0] = &b;

存储指向对象 B 的指针。只要 b 还活着,就可以通过 pointersToA[0] 访问它。


注意:最好在编译器中禁用此"扩展"以避免遇到此类问题。

pointersToA[0] = &B();

这将创建一个临时对象,调用其构造函数,将指向该对象的指针存储在 pointersToA[0] 中,然后临时对象被销毁,调用其析构函数。从这一点开始,指针ToA[]中存储的指针不再有效,并且使用它是一种未定义的行为。

B b = B();
pointersToA[0] = &b;
在这里,对象继续存在,直到此作用域

结束,使用此指针、取消引用它、调用其方法是一个有效的操作,直到作用域结束,并且对象被销毁。

您收到的错误消息是由于未定义的行为。"未定义的行为"意味着任何东西:代码仍然运行,产生正确的结果;或者代码仍在运行,产生垃圾结果;或者代码失败并出现一些随机错误;或者您的整个计算机着火并爆炸。

如果要

创建指针并存储它,则需要智能指针类或新建。您实际上正在做的是创建一个临时对象,然后将指针存储到它以前所在的位置,因此当您调用 b::d oSomething() 时,您正在调用一个已经销毁的对象。

#include <iostream>
class base {};
class derived : public base {
public:
    derived() { std::cout << "constructed" << std::endl;}
    ~derived() { std::cout << "destructed" << std::endl;}
};
int main(){
    base *pointers[3];
    pointers[0] = &derived();
    std::cout << "pointer assigned" << std::endl;
    return 0;
};

输出:

constructed
destructed
pointer assigned