I'm having trouble with virtual methods. When I call f it doesn't work. Why?
#include <iostream>
struct A {
virtual void f() const { std::cout << "In A"; }
virtual ~A() {};
};
struct B : A {
void f() const { std::cout << "In B"; }
};
int main()
{
A* a = new A();
B* b = dynamic_cast<B*>(a);
(*b).f();
delete a;
}
It doesn't print anything at all, and I don't get any errors. What did I do wrong?
What is wrong is you did not check if retuned pointer is NULL.
dynamic_cast tells you if the actual object pointed by a is of the type b, which it is obviously not. And in such a scenario it will return you a NULL.
Basically, You are dereferencing a NULL pointer, causing an Undefined Behavior which unluckily for you doesn't crash.
When you use a language provided feature it should be used in the way mandated by the standard. The use of dynamic_cast warrants a NULL check of the returned pointer.
Your pointer a should actually point to an derived class object b. You need:
A* a = new B();
B* b = dynamic_cast<B*>(a);
Also, your code must check the returned pointer:
if(b != NULL)
(*b).f();
This line:
B* b = dynamic_cast<B*>(a);
gives you a null pointer, since a does not in fact point at a B.
The following line is then Undefined Behavior. (You were "lucky" that nothing at all happened.)
Related
#include <iostream>
struct A {
virtual void a() {
puts("A");
}
};
struct B {
virtual void b() {
puts("B");
}
};
struct C {
virtual void c() {
puts("C");
}
};
struct D : public A, public B, public C {
virtual void c() {
C::c();
puts("cd");
}
};
int main() {
A* obj = new D;
obj->a();
B* b = (B*)obj;
b->b();
C* c = (C*)obj;
c->c();
return 0;
}
I have this code where I have non virtual multiple inheritance. However, it seems to call the wrong virtual function when I call the functions in the main function.
Instead of outputting:
A
B
C
cd
It outputs:
A
A
A
What puzzles me is that when I change the code to doing this:
B* b = (B*)(D*)obj;
b->b();
C* c = (C*)(D*)obj;
c->c();
It outputs what I would expect (see above). Afaik doing a double pointer cast like this wouldn't effect anything and would be optimized out by the compiler. But it seems to be changing what virtual function is being called.
Can someone explain why this would change what virtual function is being called?
Notes:
I printed the pointers at each step, they are the same.
I want to avoid using dynamic_cast (although it does work) as it's too slow for what I need it to do.
Can someone explain why this would change what virtual function is being called?
Generally, a C-style cast between pointer types won't change the value of the pointer and so will have no effect. There is, however, one exception.
A cast between a class and a parent or child class can change the value of the pointer. For example:
class A
{ int a; };
class B
{ int b; };
class C : public A, public B
...
Now, a pointer to an instance of class A will probably have the same value as a pointer to its a member and a pointer to an instance of class B will probably have the same value as a pointer to its b member. A pointer to an instance of class C can't have the same value as a pointer to both its A::a and its B::b members since they're distinct objects.
A function expecting a B* can be passed a C* since a C is a B. Similarly, a function expecting an A* can be passed a C* for the same reason. But at least one of these will require a value change to the pointer.
So casts between these types will change the values, the others are all no-ops.
Of course, all of this is UB. You are casting between unrelated types and then dereferencing them.
I want to avoid using dynamic_cast (although it does work) as it's too slow for what I need it to do.
That seems very hard to believe.
Here's the reduced code with the problem:
#include <iostream>
using namespace std;
struct A {
virtual void foo() {
cout<<"A::foo()"<<endl;
}
};
struct B : A {
void foo() {
cout<<"B::foo()"<<endl;
}
};
struct C : A {
void foo() {
cout<<"C::foo()"<<endl;
}
};
int main() {
B b[3];
A* a = b;
C c;
a[1] = c; //what's happening here??
a[1].foo(); //prints B::foo() when virtual, and A::foo() when non-virtual
}
My question is not related to dynamic or static polymorphism, but rather the strange assignment with the line a[1] = c; which seems to have totally no effect. If a is an alias to the array b, then that assignment should give at least a warning, which is absent while compiling with GCC-10.
Could anyone please clarify what's the compiler up to in that line?
From operator_arithmetic#Additive_operators (emphasize mine)
In any case, if the pointed-to type is different from the array element type, disregarding cv qualifications, at every level if the elements are themselves pointers, the behavior of pointer arithmetic is undefined. In particular, pointer arithmetic with pointer to base, which is pointing at an element of an array of derived objects is undefined.
so a[1] (which is equivalent to *(a + 1)) is undefined behavior.
You might do pointer arithmetic on b:
A* b1 = &b[1]; // OK
*b1 = c; // OK, but object slicing
even better, reference instead of pointer (no nullptr check, and pointer arithmetic is discouraged):
A& b1 = b[1]; // OK
b1 = c; // OK, but object slicing
a[1] = c;
There is object slicing in this line and undefined behavior.
Type of the a[1] is A, so object of type C sliced to the object of type A.
a[1].foo(); //prints B::foo() when virtual, and A::foo() when non-virtual
B::foo() printed because assignment operator don't change the type of object, so it will not copy pointer to vtable. vptr will point to the B::foo().
If you construct new object of type C in place of the old one, you will change vptr and function C::foo() will be called.
Just for illustration, don't do it in real code: example
Example:
#include <iostream>
using namespace std;
struct A {
virtual void foo() {
cout<<"A::foo()"<<endl;
}
};
struct B : A {
void foo() {
cout<<"B::foo()"<<endl;
}
};
struct C : A {
void foo() {
cout<<"C::foo()"<<endl;
}
};
int main() {
B b[3];
A* a = b;
C c;
// Everything below is undefined behavior and works in this example only because of struct size equality.
a[1] = c; // object slicing and undefined behavior. vptr still points to vtable of B
a[1].foo(); // B::foo()
new(b) C; // creating new object in memory ob b[0] with vptr to vtable of C
a[0].foo(); // C::foo()
}
There is no warning, because object slicing is valid operation. It can be done intentionally.
Since 'a' is a pointer to the base class A it cannot be assigned values by using the indexing concept. Variable 'a' can only point to the starting of the array or it can point to the address of a single variable.
In the above case if you want to assign variable 'c' then you can assign the address of c by the following code, which will print the C::foo() as the output.
a = &c;
a->foo();
Does C++ have existing method to check if an object is a derived-typed object? For example,
class A
{};
class B : public A
{};
A* p;
And check if p points to B.
If the class is polymorphic (i.e., has at least one virtual member function), you can use dynamic_cast or typeid.
Otherwise, no. Keeping track of an object's dynamic type has a cost, and the language was designed to avoid pessimizing code that has no need for it.
And check if p points to B.
You can use dynamic_cast for that, if there is at least one virtual member function in the class. It is common to make the destructor virtual.
class A
{
virtual ~A() {}
};
and then,
B* bPtr = dynamic_cast<B*>(p);
if ( bPtr )
{
// Use the pointer
}
Does C++ have existing method to check if an object is a derived-typed object?
There are actually two ways to achieve this:
A* p = new B();
B* pB = static_cast<B*>(p); // Checks if B is related to A in an inheritance
// ^^^^^^^^^^^^^^^^^^^ hierarchy. Fails to compile if not.
A* pA = new B();
B* pB = dynamic_cast<B*>(pA); // Checks if pA actually points to an instance of B
// ^^^^^^^^^^^^^^^^^^^^ at runtime, and returns nullptr if not
if(pB) {
// do stuff with B
}
The latter example requires you have a virtual base class:
class A {
public:
virtual ~A() {} // <<<<<<<<<<<<<<<<<<
};
If the object in question is not a polymorphic class it is possible to determine whether a class object pointed to has a specific base class at compile time. This sometimes occurs in template code where different base classes are possible.
You use std::is_base_of as follows: Note that you must also use std::remove_reference since *p is an lvalue and decltype() produces a reference.
#include <type_traits>
#include <iostream>
class A {};
class B : public A{};
int main() {
A a;
B b;
A* pa=&a;
B* pb=&b;
std::cout << std::is_base_of<A, B>::value << "\n"; // true
std::cout << std::is_base_of<B, A>::value << "\n"; // false
std::cout << std::is_base_of<A, std::remove_reference<decltype(*pb)>::type>::value << "\n"; // true
std::cout << std::is_base_of<B, std::remove_reference<decltype(*pa)>::type>::value << "\n"; // false
}
I've read about reinterpret and dynamic cast, but I saw some examples which I have questions about.
reinterpret_cast:
#include <iostream>
using namespace std;
class A
{
public:
void a() {
cout << "a";
}
};
class B: private A
{
public:
void a() {
cout << "b";
}
};
int main()
{
A *a = new A();
B *b = reinterpret_cast<B*>(a);
B *b2 = new B();
a = reinterpret_cast<A*>(b2);
b->a();
a->a();
return 0;
}
Would print ba.
My explanation was that reinterpret_cast change the bit pattern, and both types has a funtion called a() so that was the result.
Then I saw this:
using namespace std;
class B;
class A
{
private:
int j = 4;
public:
A() {}
A(const B &b) {}
void a() {
cout << j << endl;
}
};
class B
{
private:
int i = 5;
public:
B() {};
B(const A &a) {}
void a() {
cout << i << endl;
}
};
int main()
{
A *a = new A();
B *b = reinterpret_cast<B*>(a);
B *b2 = new B();
a = reinterpret_cast<A*>(b2);
b->a();
a->a();
return 0;
}
and that printed 45. I guess it has something with inheritance but I don't know how or why.
About dynamic cast:
#include <iostream>
using namespace std;
class A {
public:
virtual ~A(){}
};
class B {
public:
void a() {
cout << "B" << endl;
}
virtual ~B() {}
};
int main()
{
A *a = new A();
dynamic_cast<B*>(a)->a();
return 0;
}
That would print "B".
But if I would write:
virtual void a() {
cout << "B" << endl;
}
I would get segmentation fault.
Why I got the result I got in both examples?
Thanks for all your help!
In the first case, you're basically lying to the compiler and telling it to pretend a pointer to an A is a pointer to a B without doing necessary conversions. However, it doesn't matter because the function isn't virtual, so it just calls the function based on the pointer type.
In the second case, the dynamic cast fails because the two types are unrelated. but you still invoke B::a, just on no object. That causes no problem because no attempt to access the object takes place.
In the third case, the dynamic cast fails again. But since the function is virtual, executing it requires accessing the object to determine its fully-derived type. Since there is no object (no instead of a B exists), that fails.
The object pointed to has a compile-time type and a run-time type. You need to understand how each of those affects the action taken.
When you do a reinterpret_cast you have changed the compile-time type but have not changed the run-time type.
When you call a non virtual function, which class you get that function from depends only on the compile-time type not the run-time type. That function then assumes the run-time type is either the same as the compile-time type or derived from it. That assumption is false in your first example, which makes the behavior undefined in theory. But in practice, the function doesn't actually use the object, so the object being the wrong type has no consequence.
In your second example, you still call the function in the class of your compile-time type. Your testing hides that fact, so you may be confused about that. But the object is of the run-time type so its data is as initialized in the run-time type. That data is accessed by position, not by name. So the use of i gets the actual value of j (through very undefined behavior) because it has the same position. I expect that caused you to be confused over which function was called. You could make that example easier to understand if you changed:
cout << i << endl;
to
cout << "i == " << i << endl;
In your third example, the dynamic_cast still unconditionally changes the compile-time type to the requested type. But in case the requested type cannot be correctly reached from the run-time type of the actual object (as is true in your example of unrelated classes) the pointer itself is null. So when you then call a function that doesn't actually use the object, the result is technically undefined but in practice executes as the compile-time type. But when you call a virtual function, the call itself uses the object and since the object pointer is null, that will seg fault.
class A
{
public:
virtual void func() {
cout<<" func():: in class A"<< endl;
}
void func1(){
cout<<"func1():: in class A";
}
};
class B: public A {
public:
void func() {
cout<<" func():: in class B"<< endl;
}
void func1(){
cout<<"func1():: in class B";
}
};
int main()
{
A a;
A* pa = &a;
B* pb = dynamic_cast<B*>(pa);
pb->func1();
return 0;
}
Though pb is pointing to an incomplete type and dynamic_cast will return null.
But why it is not crashing in this scenario?
Since func1 in your B class doesn't access any member variables (or virtual functions) it doesn't use the implicit this pointer and therefore, in this case, does not crash.
Note that this is undefined behaviour so it may crash (or do something unexpected) in a different compiler (or compiler version) so don't depend on this behaviour.
You have a A * that is pointing to an instance of A, not A * that is actually pointing to an instance of B. That is why dynamic_cast returns a null pointer of type B *, it is not related to these being incomplete types or anything (in the linked code, both A and B are complete types), and thus this is defined behaviour for dynamic_cast. However after that a null pointer is accessed; a clever compile can know that dynamic_cast can fail at that point and the pb->func1(); can do anything including not causing a null pointer exception or anything at all, or even calling the pb1->func on something that is not B.
An example with an incomplete type would be:
#include <iostream>
using namespace std;
class A {
public:
virtual void func() {
cout << "func():: in class A" << endl;
}
void func1(){
cout<< "func1():: in class A";
}
};
class B;
int main() {
A a;
A* pa = &a;
B* pb = dynamic_cast<B*>(pa);
return 0;
}
Now if you compile this with G++, you get
y.cc: In function ‘int main()’:
y.cc:20:32: error: cannot dynamic_cast ‘pa’ (of type ‘class A*’) to
type ‘class B*’ (target is not pointer or reference to complete type)
B* pb = dynamic_cast<B*>(pa);
that is all sane C++ compilers will reject such code.