I think I know the reason why the non virtual example didn't crash. The funcA() function does not use any state inside the A1 class. For the non virtual example, the program is just calling a function without really needing a valid A1 pointer. In the polymorphic example, A1 actually contains some "invisible" data (vpointer) that identifies the type of A1, because A1 inherits from A.
The program tries to access that vpointer and realizes the pointer is invalid. Now, I wonder why this happens because A1::funcA() is not declared as virtual and the function call inside main is using an A1 pointer, not the parent A pointer. So the compiler knows it needs to call A1::funcA() in example 2. I wonder why the compiler is not calling the funcA() the same way as in the non virtual example. Maybe someone else can explain this.
But in any case, never call a function on an invalid pointer.
> The funcA() function does not use any state inside the A1 class.
> For the non virtual example, the program is just calling a function without really needing a valid A1 pointer.
> In the polymorphic example, A1 actually contains some "invisible" data (vpointer)
nkendra +1
> I wonder why the compiler is not calling the funcA() the same way as in the non virtual example.
There is a quality of implementation issue involved. The pointer obtained is from the innards of a vector; most compilers wouldn't do the huge amount of static analysis required to determine that the buffer held by the vector is not aliased, and therefore a1[0] must always be a pointer to an object with A as its dynamic type.
In a simpler situation, with no vector (and allocator) involved, the compiler would typically be able to elide the run-time vtbl lookup.
/* g++ -std=c++11 -Wall -Wextra -pedantic-errors -O3 -fomit-frame-pointer -c -S test.cc */
/* .ident "GCC: (GNU) 4.9.0 20131020 (experimental)" */
struct A
{
virtual ~A() {}
virtualint value() const { return 7 ; }
};
int foo()
{
A a ;
A& ra = a ;
return a.value() ; // inlined; resolved at compile time to A::value()
/*
__Z3foov:
movl $7, %eax
ret
*/
}
int bar()
{
A a ; // local object, not aliased
A* pa = &a ; // it is known at compile time that pa always points to 'a'
return pa->value() ; // inlined; resolved at compile time to A::value()
/*
__Z3barv:
movl $7, %eax
ret
*/
}
int baz()
{
A* pa = new A ; // it is known at compile time that pa always points to 'a'
return pa->value() ; // inlined; resolved at compile time to A::value()
/*
subl $28, %esp
movl $4, (%esp)
call __Znwj ; call ::operator new
movl $__ZTV1A+8, (%eax)
movl $7, %eax ; inlined
addl $28, %esp
ret
*/
}
Thanks for the explanation JLBorges. My thinking was that, since A1::funcA() is not declared virtual, one (and only one) function can be called by vector<A1*>[i]->funcA().
1 2 3 4 5 6 7 8 9
vector<A1*> v;
v.push_back((A1*)new A2);
v[0]->funcA(); // I would think this could only call A1::funcA()
// or how about
v.push_back((A1*)&someWeirdType);
v[1]->funcA();
> My thinking was that, since A1::funcA() is not declared virtual,
> one (and only one) function can be called by vector<A1*>[i]->funcA().
Yes. The issue of compile-time determination of the dynamic type of the object pointed to (to optimize away the cost of run-time look up) arises only if the function is virtual. If it is not virtual, one (and only one) function can be called.
1 2 3
> // or how about
> v.push_back((A1*)&someWeirdType);
> v[1]->funcA();
Depends on what someWeirdType is.
If it is an unrelated type, the cast is a reinterpret_cast and the result is undefined behaviour.