Laura
Laura

Reputation: 279

List insert iterator outside range (C++)

I have a class A that can respond to doSomething() and some subclasses B, C and D that all override doSomething(). B particularly has an instance variable that is a list from the standard library and adds elements to such list when it responds to doSomething().

In some part of my code, I create an array with pointers to objects of type A. Now, If I declare it this way:

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

I get a List insert iterator outside range when calling pointersToA[0]->doSomething().

But if I do it this way:

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

Everything works as expected.

Why does this happen?

Upvotes: 0

Views: 894

Answers (4)

Bartek Banachewicz
Bartek Banachewicz

Reputation: 39370

Taking an address of an rvalue is pretty much guaranteed to cause problems sooner or later (typically sooner), and is actually illegal in C++ (so your compiler isn't exactly top-notch on that). An address isn't a strong reference to an object; since C++ is not a garbage-collected language, the values of B, C and D disappear as soon as the statements they are used in are executed.

For your use dynamic allocation sounds like the most straighforward way:

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

Upvotes: 6

juanchopanza
juanchopanza

Reputation: 227400

When you do this:

pointersToA[0] = &B();

you are storing a pointer to a temporary in pointersToA[0]. This is invalid in standard C++, and a conforming implementation should emit an error. The fact that this compiler for you suggests your compiler has either a bug or a non-standard extension.

Given that your compiler accepts that code, the issue is that after that line, pointersToA[0] is invalidated because the B object it points to no longer exists. De-referencing that pointer is undefined behaviour.

When you do this

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

you store a pointer to object b. As long as b is alive, it is OK to access it via pointersToA[0].


Note: It would be a good idea to disable this "extension" in your compiler to avoid running into this kind of problem.

Upvotes: 5

Kenny Ostrom
Kenny Ostrom

Reputation: 5871

If you want to create a pointer and store it, you will need a smart pointer class or new. What you are actually doing is creating a temporary object, and then storing the pointer to where it used to be, so when you call b::doSomething(), you are calling an already destructed object.

#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;
};

output:

constructed
destructed
pointer assigned

Upvotes: 0

Sam Varshavchik
Sam Varshavchik

Reputation: 118330

pointersToA[0] = &B();

This creates a temporary object, calls its constructor, stores a pointer to the object in pointersToA[0], then the temporary object gets destroyed, invoking its destructor. From this point, the stored pointer in pointersToA[] is no longer valid, and using it is undefined behavior.

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

Here, the object continues to exist, until the end of this scope, and using this pointer, dereferencing it, calling its method, is a valid operation, until the end of the scope, and the object gets destroyed.

The error message you were getting is due to undefined behavior. "Undefined behavior" means anything: the code still runs, produces the correct result; or the code still runs, produces garbage results; or the code fails with some random error; or your entire computer catches fire, and explodes.

Upvotes: 1

Related Questions