I'm having trouble understanding the value of this in the following example:
struct A {
int i;
void bar() {
cout << this << endl;
}
};
struct B : public A {
virtual void foo() = 0;
};
struct C : public B {
void foo() {
printf("hello world!\n");
}
};
int main (int argc, const char* argv[]) {
C* c = new C;
cout << c << endl;
c->bar();
return 0;
}
Both times I print the pointer to the console, I get different values. I would expect it to be the same, as they refer to the same instance both times?!
If I remove either the virtual function or the int i in A it goes away. Why?
But it refers to differnt things.
void bar() {
cout << this << endl;
}
Here this has a type A*. It points to the part of the object that is A.
out << c << endl;
Here c is a C* and points to the part of the object that is C.
If the A object lines up exactly with the C object the pointers are the same. If they do not (like when C contains other members (hidden pointer to the vtable) and thus the 'A' part is offset from the start of the larger object) then the pointers are not necessarily the same.
It is a compiler optimization. The A struct doesn't have any virtual methods so it doesn't need a v-table. No point storing a pointer to it in an object of type A. Which makes the object layout different between objects of type A and C since C does need a v-table. The compiler makes up the difference by incrementing this before calling a method of A.
Add an arbitrary virtual method to A to make the pointers match.
Related
I'm trying to figure out why b->boo() actually calls a.far().
is the multiple inheritance from template class and general class forbidden? why does the inherit order matter?
The code is here:
#include <iostream>
template <int somecount>
class inner_parent_class
{
public:
int array[somecount];
virtual void far() = 0;
};
class any_class
{
public:
virtual void boo() = 0;
};
template <int somecount>
class child_class_bad : public inner_parent_class<somecount>, public any_class
{
public:
virtual void boo() override
{
std::cout << "call me" << std::endl;
}
virtual void far() override
{
std::cout << "do not call me" << std::endl;
}
};
template <int somecount>
class child_class_good : public any_class, public inner_parent_class<somecount>
{
public:
virtual void boo() override
{
std::cout << "call me" << std::endl;
}
virtual void far() override
{
std::cout << "do not call me" << std::endl;
}
};
int main()
{
{
child_class_good<32> a;
any_class* b;
auto c = dynamic_cast<void*>(&a);
b = reinterpret_cast<any_class*>(c);
b->boo();
}
{
child_class_bad<32> a;
any_class* b;
auto c = dynamic_cast<void*>(&a);
b = reinterpret_cast<any_class*>(c);
b->boo();
}
return 0;
}
# GCC 9.3.0
# VS 2019 16.5.3
I suppose that both child classes (child_class_good and child_class_bad) are different classes even though their class names are the same, because they are template classes and constructed separately at compiled time. Nevertheless, each class might have its own v-table, so I think calling boo() as their common parent class any_class should correctly work.
reinterpret_cast cannot be used to do what you're trying to do. A reinterpret_cast from a void* to a T* only produces a pointer to a valid T* if the void* pointer it was given was a pointer to an object of type T.
Doing a dynamic_cast<void*>(p) returns a void* which points to the most-derived object pointed to by p. Since your &a is in fact the most-derived object that it points to, it simply converts the pointer to a void*.
Then you perform reinterpret_cast<any_class*> on that void*. The void* points to an object of type child_class_good<32> or child_class_bad<32>. Your cast is saying that the pointer actually points to an any_class. This is incorrect (neither type is standard layout, so the layout of the base classes is not defined), and thus attempting to use the results will yield undefined behavior.
The case that you identify as good is just as invalid as bad; it merely happens to work.
It is not clear why you're trying to do whatever it is you're trying to do, but there's no valid way to take a void* pointing to the most-derived object of an unknown type and casting it to anything useful. In order to use a void*, you have to know the exact type that was used to produce that void*.
The library I am using defines an abstract base class A with ~10 pure virtual methods. There are no non-pure methods publicly defined and I suspect that A has no private data, i.e. the class is merely an interface. The library also defines a few concrete sub-classes of A, such as C.
I would like to add some functionality to A so that all of its sub-classes inherit this functionality. However, A is defined in the library and I cannot do this.
Instead, I have defined a new sub-class of B. This sub-class is not concrete and all of the pure virtual methods of A are left alone. It defines a few new helper methods that call out to the methods defined by A. There are also no fields defined by B.
In order to use B to augment the functionality of instance of C I have done the following, which I suspect is not guaranteed behavior.
Create an object of type C on the heap.
Cast the pointer to this object to instead be of type B*.
Return this pointer, exiting the local scope where the object was created.
Call methods defined by B using this pointer in a different scope.
Is this safe? The objects are created on the heap, so I don't think any slicing will happen. If the safety depends on which fields A and C define, what requirements are needed to guarantee this behavior?
If neither A nor C had their own data other than the vpointer, would this be safe?
What about if only C has its own data?
Edit: I should add that I have attempted this and the behavior has at least seemed to be what I want. I have not profiled for memory leaks though.
The whole thing looks like code smell to me. I would take the approach for B class of a wrapper class: take an A* pointer in the constructor, forwarding the calls you need to that A*. Then you can pass a C* to that constructor, that will be correctly deleted through a "delete" in B destructor.
Here is one idea following up on my comment. It behaves as intended, might be some hidden dangers but to me it seems reasonable as long as bar does not hold any data.
#include <iostream>
struct base {
int x = 1;
int y = 2;
void show() {
std::cout << x + y << std::endl;
}
virtual void virt() = 0;
};
struct foo : base {
int a = 5;
int b = 2;
void print() {
std::cout << a + b << std::endl;
x++;
}
void virt() override {
std::cout << "foo" << std::endl;
}
};
template <typename T>
struct bar : T {
void print2() {
std::cout << T::a * T::b << std::endl;
T::b++;
T::x++;
T::virt();
}
};
template <typename Base>
bar<Base>* extend_with_bar(Base& base) {
return static_cast<bar<Base>*>(&base);
}
int main() {
foo f;
f.show();
f.print();
auto b = extend_with_bar(f);
b->print2();
b->print2();
b->print();
f.print();
b->show();
f.show();
b->virt();
}
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.
This question refers to common problems discussed in these questions:
Can virtual functions have default parameters?
Virtual functions default parameters
Here is what currently happens in c++ with default parameters to virtual functions:
struct Base
{
virtual void foo(int one = 1, int two = 2)
{ cout << "one: " << one << " two: " << two << endl; }
};
struct Derived : public Base
{
virtual void foo(int one = 3, int two = 4)
{ Base::foo(one, two); cout << " derived!" << endl; }
};
int main()
{
Base* b = new Base();
Base* d = new Derived();
Derived* dp = new Derived();
b->foo();
d->foo();
dp->foo();
return 0;
}
output:
one: 1 two: 2
one: 1 two: 2
derived!
one: 3 two: 4
derived!
This is the behaviour I wish existed in c++ virtual function default parameters:
#include <iostream>
using namespace std;
struct Base
{
virtual void foo () { foo(1, 2); }
virtual void foo (int one) { foo(one, 2); }
virtual void foo(int one, int two)
{ cout << "one: " << one << " two: " << two << endl; }
};
struct Derived : public Base
{
virtual void foo() { foo(3, 4); }
virtual void foo(int one, int two)
{ Base::foo(one, two); cout << " derived!" << endl; }
};
int main()
{
Base* b = new Base();
Base* d = new Derived();
Derived* dp = new Derived();
b->foo();
d->foo();
dp->foo();
return 0;
}
output:
one: 1 two: 2
one: 3 two: 4
derived!
one: 3 two: 4
derived!
So, basically, if I want to override a default parameter in a parent class, I will just create a new foo with that number of arguments. Note that the derived overrides the no-arg condition, but not the one-arg condition.
As a side note, my current project uses a purely virtual base class. I often have pointers of the base class type and of the derived class type. I want a call from either pointer to have the same result.
QUESTIONS:
I have read many questions related to this subject, but they all don't seem to give reasonable solutions. Most of the solutions would result in uglier code all throughout your project.
Some say "Don't use default parameters on virtual functions," but then I would need to put the default in every place I call the function. It seems it would be better to just put a comment that says, "also change base class" and "also change derived class" than to have to change everywhere the function is called.
Some say to only have the default parameters in the base class, but that means that any pointer to a derived object would need to be cast back to a base pointer before the defaults could be used. That makes a lot of code ugly as well.
Are there reasons I should avoid the above design?
At some point in time future maintainers of your code will be baffled, confused, and/or perplexed if you change the default values depending on which static type they call foo on so I'm going to assume that's not your concern.
Given that your concern is someone changing the default in the parent and forgetting to update the child class that's easily solved with the non-virtual interface pattern:
#include <iostream>
using namespace std;
struct Base
{
void foo(int one = 1, int two = 2) { foo_impl(one, two); }
protected:
virtual void foo_impl(int one, int two)
{ cout << "one: " << one << " two: " << two << endl; }
};
struct Derived : public Base
{
protected:
virtual void foo_impl(int one, int two)
{ Base::foo_impl(one, two); cout << " derived!" << endl; }
};
int main()
{
Base* b = new Base();
Base* d = new Derived();
Derived* dp = new Derived();
b->foo();
d->foo();
dp->foo();
return 0;
}
I think the problem in "understanding what's going on" is that default values for function arguments are resolved at compile-time - this means that unless it's VERY obvious and simple, the compiler will not KNOW what class some pointer is pointing at, and won't give the right argument. In other words, the compiler will use the class of the pointer when determining the arguments for your function. There is absolutely no way you can work around this (other than "knowing which class you want to use", but then it's pretty meaningless to use virtual functions).
The solution depends on what you REALLY want to do, but an obvious answer is to NOT use default arguments. The other solution is, like you have, some sort of indirect function call, where the "no arguments" function is different from the one and two argument functions.
But it's perhaps also a good idea to wonder if your design is right in the first place. Maybe you need to find a different solution somewhere...
First of all, you are not using default parameters in your example. Thus, the problem that usual default parameters have do not apply to your code. In your code, subclasses can override default parameters which will work as expected no matter if a Derived is used through a Base or a Derived pointer. Thus, I do not see a problem with your approach, it seems fine to me.
Lets say we have the following two class definitions.
#include <iostream>
#include <array>
class A
{
public:
virtual void f() = 0;
};
class B : public A
{
public:
virtual void f() { std::cout << i << std::endl; }
int i;
};
Here sizeof(B) == 8, presumably 4 the virtual pointer and 4 for the int.
Now lets say we make an array of B, like so:
std::array<B, 10> x;
Now we get sizeof(x) == 80.
If my understanding is correct, all method calls on elements of x are resolved statically, as we know the type at compile time. Unless we do something like A* p = &x[i] I don't see a need to even store the virtual pointer.
Is there a way to create an object of type B without a virtual pointer if you know it is not going to be used?
i.e. a template type nonvirtual<T> which does not contain a virtual pointer, and cannot be pointed to by a subtype of T?
Is there a way to create an object of type B without a virtual pointer if you know it is not going to be used?
No. Objects are what they are. A virtual object is virtual, always.
After all, you could do this:
A *a = &x[2];
a->f();
That is perfectly legitimate and legal code. And C++ has to allow it. The type B is virtual, and it has a certain size. You can't make a type be a different type based on where it is used.
Answering my own question here, but I've found that the following does the job, by splitting A into it's virtual and non-virtual components:
enum is_virtual
{
VIRTUAL,
STATIC
};
template <is_virtual X>
class A;
template<>
class A<STATIC>
{
};
template<>
class A<VIRTUAL> : public A<STATIC>
{
public:
virtual void f() = 0;
virtual ~A() {}
};
template <is_virtual X>
class B : public A<X>
{
public:
void f() { std::cout << i << std::endl; }
int i;
};
The important thing here is that in B<> don't specify f() as virtual. That way it will be virtual if the class inherits A<VIRTUAL>, but not virtual if it inherits A<STATIC>. Then we can do the following:
int main()
{
std::cout << sizeof(B<STATIC>) << std::endl; // 4
std::cout << sizeof(B<VIRTUAL>) << std::endl; // 8
std::array<B<STATIC>, 10> x1;
std::array<B<VIRTUAL>, 10> x2;
std::cout << sizeof(x1) << std::endl; // 40
std::cout << sizeof(x2) << std::endl; // 80
}
That would be a nice one to have, but I can't think of any way to revoke virtual members or avoid storing the virtual pointer.
You could probably do some nasty hacks by keeping a buffer that's the size of B without the virtual pointer, and play with casts and such. But is all undefined behavior, and platform dependant.
Unfortunately it won't work in any normal sense as the code inside the method calls expects the virtual pointer to be in the class definition.
I suppose you could copy/paste all of A and B's code into a new class but that gets to be a maintenance headache fast.