Reputation: 279
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
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
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
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
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