I read the following from C++ Primer (5th edition, Section 18.1.1):
"When we throw an expression, the static, compile-time type of that expression determines the type of the exception object." So I tried the following code:
#include <iostream>
class Base{
public:
virtual void print(std::ostream& os){os << "Base\n";}
};
class Derived: public Base{
public:
void print(std::ostream& os){os << "Derived\n";}
};
int main(){
try{
Derived d;
Base &b = d;
b.print(std::cout); //line 1
throw b;
}
catch(Base& c){
c.print(std::cout); //line 2
}
return 0;
}
which gives me the following output:
Derived
Base
I think I understand why this output is expected: at line 1, we have dynamic binding. Now when we throw b, it is based on the static type of b, which means both the static type and the dynamic type of c is Base&, and therefore we see the result at line 2.
However, if I were to use a pointer, instead of a reference:
int main(){
try{
Derived d;
Base *b = &d;
b->print(std::cout); //line 1
throw b;
}
catch(Base* c){
c->print(std::cout); //line 2
}
return 0;
}
the output now becomes:
Derived
Derived
which seems to imply that the static type of c is Base*, but the dynamic type of c is Derived*, why? Shouldn't both the static and the dynamic types of c be Base*?
When we throw an expression, the static, compile-time type of that expression determines the type of the exception object
The above is entirely true. What you forget, is that pointers are objects too. And when you throw a pointer, that's your exception object.
The object you should have dynamically1 allocated is still pointed to by that Base* pointer. And no slicing occurs on it, because there is no attempt to copy it. As such, dynamic dispatch via-pointer accesses a Derived object, and that object will use the overriding function.
This "discrepancy" is why it is usually best to construct the exception object in the throw expression itself.
1 That pointer points to a local object, you did a big no no there and got yourself a dangling pointer.
In first case you are throwing a fresh instance of Base class invoking a copy constructor because you are passing a reference to Base into throw operator.
In second case you are throwing a pointer to a stack-allocated object of type Derived that goes out of scope when exception is thrown so then you capture and then dereference a dangling pointer causing Undefined Behavior.
First scenario
I think that if you add some prints to your classes, you could see a clearer picture:
struct Base {
Base() { std::cout << "Base c'tor\n"; }
Base(const Base &) { std::cout << "Base copy c'tor\n"; }
virtual void print(std::ostream& os) { std::cout << "Base print\n"; }
};
struct Derived: public Base {
Derived() { std::cout << "Derived c'tor\n"; }
Derived(const Derived &) { std::cout << "Derived copy c'tor\n"; }
virtual void print(std::ostream& os) { std::cout << "Derived print\n"; }
};
And the output is:
Base c'tor
Derived c'tor
Derived print
throwing // Printed right before `throw b;` in main()
Base copy c'tor
Base print
As you can see, when calling throw b; there is a copy construction of a different temporary Base object for the exception. From cppreference.com:
First, copy-initializes the exception object from expression
This copy-initialization slices the object, as if you assigned Base c = b
Second scenario
First, you are throwing a pointer to a local object, causing undefined behavior, please avoid that by all means!
Let's say you fix that, and you throw a dynamically allocated pointer, it works since you are throwing a pointer, which doesn't affect the object and preserves dynamic type information.
Related
#include <iostream>
class A
{
public:
virtual ~A() = default;
virtual void foo(void) = 0;
};
class B : public A
{
private:
int x;
public:
B(int a) : x(a) {}
void foo(void) { std::cout << "B: " << x << "\n"; }
};
class Foo
{
private:
A* a_ptr;
public:
Foo (B& x) { a_ptr = &x; }
A* get_ptr(void) { return a_ptr; }
void dummy(void) { std::cout << "Foo: "; std::cout << a_ptr << "\t "<< typeid(*a_ptr).name() << "\n"; a_ptr->foo(); std::cout << "\n"; }
};
int main(void)
{
B b(10);
Foo f(b);
f.dummy();
return 0;
}
If the constructor of Foo takes a reference to an object of B, then this program executes the way I expect it to, i.e. a_ptr->foo() calls B::foo().
However, if the constructor is changed to accept the parameter by value, then a_ptr->foo() resolves to A::foo(), and results in a pure virtual method called exception
Sample output (Passed by reference:):
Foo: 0x7fffe90a24e0 1B
B: 10
Sample output (Passed by value):
Foo: 0x7fffc6bbab20 1A
pure virtual method called
terminate called without an active exception
Aborted (core dumped)
I've a vague hunch as to why this might be happening, and I'm looking for some literature or reference which might prove or disprove my hypothesis: When passed by reference, the base class pointer a_ptr points to an entity whose lifetime exceeds past the call to a_ptr->foo().
However, when passed by value, a_ptr points to a temporary which is lost when the constructor exits.
I suppose this has something to do with the VTABLE of A, but I can't quite put my finger on it.
Yes, your suspicion is correct.
When the B object is passed by value into the Foo constructor, it becomes a local variable of the constructor. The constructor is saving a pointer to that local object, which goes out of scope when the constructor exits.
So, the call to a_ptr->foo() in Foo::dummy() is actually undefined behavior since a_ptr doesn't even point at a valid object to begin with. But, it doesn't really crash since A::foo() doesn't use its this pointer for anything. It just points to a compiler-defined function that throws the pure virtual method called error, which you don't catch, so your program terminates.
You assigned temporary object B by reference to a_ptr which is of type A*. On constructor exit this temporary object has beed destroyed. As VTABLE has been destroyed too, called A::foo, which is pure virtual. So you got it.
struct base
{
base(){}
~base() { cout << "base destructor" << endl; }
};
struct derived : public base
{
derived() : base() { vec.resize(200000000); }
~derived() { cout << "derived destructor" << endl; }
vector<int> vec;
};
int main()
{
base* ptr = new derived();
delete ptr;
while (true)
{
}
}
The above code leaks due to delete operation not calling derived object's destructor. But...
struct base
{
base() {}
~base() { cout << "base destructor" << endl; }
};
struct derived : public base
{
derived() : base() {}
~derived() { cout << "derived destructor" << endl; }
int arr[200000000];
};
int main()
{
base* ptr = new derived();
delete ptr;
while (true)
{
}
}
In second case, the memory doesn't leak despite the base destructor is only being called. So I'm assuming it's safe to not have a base destructor if all my members are automatic variables? Doesn't 'arr' member in derived class never go out of scope when derived object's destructor is not being called? What's going on behind the scenes?
YES!
I see that you are thinking "practically", about what destructions might be missed. Consider that the destructor of your derived class is not just the destructor body you write — in this context you also need to consider member destruction, and your suggestion may fail to destroy the vector (because the routine non-virtually destroying your object won't even know that there is a derived part to consider). The vector has dynamically allocated contents which would be leaked.
However we don't even need to go that far. The behaviour of your program is undefined, period, end of story. The optimiser can make assumptions based on your code being valid. If it's not, you can and should expect strange sh!t to happen that may not fit with how your expectation of a computer should work. That's because C++ is an abstraction, compilation is complex, and you made a contract with the language.
It is always necessary to have a virtual destructor in a base class if a derived object is ever deleted through a pointer to that base. Otherwise behaviour of the program is undefined. In any other case it is not necessary to have a virtual destructor. It is irrelevant what members the class has.
It's not necessary to have a memory leak and still invoke an UB. Memory leak is a kind of expected UB if your derived class isn't trivial. Example:
#include <iostream>
class Field {
public:
int *data;
Field() : data(new int[100]) {}
~Field() { delete[] data; std::cout << "Field is destroyed"; }
};
class Base {
int c;
};
// Derived class, contains a non-trivial non-static member
class Core : public Base
{
Field A;
};
int main()
{
Base *base = new Core;
delete base; // won't delete Field
}
he C++ Standard, [expr.delete], paragraph 3 states (2014 edition)
In the first alternative (delete object), if the static type of the
object to be deleted is different from its dynamic type, the static
type shall be a base class of the dynamic type of the object to be
deleted and the static type shall have a virtual destructor or the
behavior is undefined. In the second alternative (delete array) if the
dynamic type of the object to be deleted differs from its static type,
the behavior is undefined.
In reality , if base class is trivial, all fields are trivial and derived class contains no non-static or non-trivial members, one might argue, that those classes are equal, but I'm yet to find way how to prove that through standard.It's likely an IB instead of UB.
I have a function that accepts a shared pointer of type Base and then std::dynamic_pointer_cast to a derived type. However, the derived pointer is a NULL and I can't see why. I have made sure to include a virtual destructor in my base class. I do not want to use a static cast as this cannot guarantee that my derived member variables and functions are preserved?
The code is as follows:
Base Class:
class Base
{
public:
mType get_type()
{
return msg_type;
}
void set_type(mType type)
{
msg_type = type;
}
virtual ~cMsg() = default;
protected:
mType msg_type;
message msg;
};
Derived Class:
class Derived : public Base
{
public:
void set_i(int j)
{
i = j;
}
int get_i()
{
return i;
}
private:
int i;
};
Function performing cast:
void callback(std::shared_ptr<Base> msg_received)
{
std::cout<< "Callback called\n";
auto real_msg = std::dynamic_pointer_cast<Derived>(msg_received);
if (real_msg != NULL)
{
std::cout << "i value is: " << real_msg->get_i() << "\n";
}
}
Function creating the Derived object and calling the function:
int main()
{
Derived test_msg;
test_msg.set_i(1);
test_msg.set_type(mSystem::TEST_MSG);
std::shared_ptr<Base> msg_ptr = std::make_shared<Base>(test_msg);
callback(msg_ptr);
return 0;
}
Any help would be appreciated.
Edit: Corrected typo
You're totally slicing. Your dynamically allocated object (managed by that shared pointer) is just a Base, copy constructed from a Derived. You can't "get the Derived back" afterwards any more than you can design a sports car with construction plans only for one of its wheels.
I think your key confusion here is in thinking that make_shared makes something shared. It doesn't. It makes something new that will be shared.
You are making shared_ptr of type Base, even if you provide it with object of type Derived. Keep in mind, all make_shared does (after allocating memory using allocator) is calling a constructor of type specified with the arguments provided to make_shared.
In your case, it creates an object of type Base, and gives it an instance of Derived. Base copy constructor is than called, passed Derived object implicitly converted to the const reference to Base.
I have the following code (stolen from virtual functions and static_cast):
#include <iostream>
class Base
{
public:
virtual void foo() { std::cout << "Base::foo() \n"; }
};
class Derived : public Base
{
public:
virtual void foo() { std::cout << "Derived::foo() \n"; }
};
If I have:
int main()
{
Base base;
Derived& _1 = static_cast<Derived&>(base);
_1.foo();
}
The print-out will be: Base::foo()
However, if I have:
int main()
{
Base * base;
Derived* _1 = static_cast<Derived*>(base);
_1->foo();
}
The print-out will be: Segmentation fault: 11
Honestly, I don't quite understand both. Can somebody explain the complications between static_cast and virtual methods based on the above examples? BTW, what could I do if I want the print-out to be "Derived::foo()"?
A valid static_cast to pointer or reference type does not affect virtual calls at all. Virtual calls are resolved in accordance with the dynamic type of the object. static_cast to pointer or reference does not change the dynamic type of the actual object.
The output you observe in your examples is irrelevant though. The examples are simply broken.
The first one makes an invalid static_cast. You are not allowed to cast Base & to Derived & in situations when the underlying object is not Derived. Any attempt to perform such cast produces undefined behavior.
Here's an example of valid application of static_cast for reference type downcasting
int main()
{
Derived derived;
Base &base = derived;
Derived& _1 = static_cast<Derived&>(base);
_1.foo();
}
In your second example the code is completely broken for reasons that have nothing to do with any casts or virtual calls. The code attempts to manipulate non-initialized pointers - the behavior is undefined.
In your second example, you segfault because you did not instanciate your base pointer. So there is no v-table to call. Try:
Base * base = new Base();
Derived* _1 = static_cast<Derived*>(base);
_1->foo();
This will print Base::foo()
The question makes no sense, as the static_cast will not affect the v-table. However, this makes more sens with non-virtual functions :
class Base
{
public:
void foo() { std::cout << "Base::foo() \n"; }
};
class Derived : public Base
{
public:
void foo() { std::cout << "Derived::foo() \n"; }
};
int main()
{
Base base;
Derived& _1 = static_cast<Derived&>(base);
_1.foo();
}
This one will output Derived::foo(). This is however a very wrong code, and though it compiles, the behavior is undefined.
The whole purpose of virtual functions is that the static type of the variable shouldn't matter. The compiler will look up the actual implementation for the object itself (usually with a vtable pointer hidden within the object). static_cast should have no effect.
In both examples the behavior is undefined. A Base object is not a Derived object, and telling the compiler to pretend that it is doesn't make it one. The way to get the code to print out "Derived::foo()" is to use an object of type Derived.
This question is very similar to this one Why can't I dynamic_cast "sideways" during multiple inheritence?, except that the cast does work - just not inside in the constructor.
Header:
class A
{
public:
virtual ~A() {}
void printA();
};
class B
{
public:
B();
virtual ~B() {}
void printB();
private:
std::string message_;
};
class C : public A, public B
{
public:
C() {}
virtual ~C() {}
};
Source:
void A::printA() { cout << "A" << endl; }
B::B()
{
A* a = dynamic_cast< A* >( this );
if ( a ) {
message_ = std::string( "A and B" );
} else {
message_ = std::string( "B" );
}
}
void B::printB() { cout << message_.c_str() << endl; }
Main:
int main( int argc, char* argv[] )
{
cout << "Printing C..." << endl;
C c;
c.printA();
c.printB();
cout << "Checking again..." << endl;
cout << !!dynamic_cast< A* >( &c ) << endl;
return EXIT_SUCCESS;
}
Result:
Printing C...
A
B
Checking again...
1
So, the dynamic_cast does work for multiple inheritance (no surprises there!), but why not when called at runtime for the 'this' pointer inside B::B()? I thought that the object was fully formed once inside the body of the constructor i.e. all the memory was allocated for the component objects, they haven't been initialised yet. I appreciate that this depends on the superclass constructor order, but in this example A is called before B.
I am obviously not understanding what exactly is happening under the hood, can someone please enlighten me?
Thanks,
Cam Bamber.
Basically the standard says it will not work (dynamic_cast) during construction of an object.
<quote>
Edit: Added based on VJo comment below.
Note: The cast from a 'B' to an 'A' using dynamic cast should work because we are casting an object of type 'C'. If we added the following code to main:
B bObj;
B& bRef = c;
B* bPtr = &c;
std::cout << !!dynamic_cast<A*>(&bObj) << std::endl;
std::cout << !!dynamic_cast<A*>(&bRef) << std::endl;
std::cout << !!dynamic_cast<A*>( bPtr) << std::endl;
The extra output would be:
0 // Can not convert a B to an A
1 // Can convert this B to an A because it is really a C.
1 // This is what we are reeling doing in B::B() that fails
// It is not the dynamic_cast<> that fails but the conversion of this from C* to B*
// That is causing UB
It fails in the constructor because the object is not fully formed. Using this we are trying to convert a C pointer into a B pointer before the C constructor has started (the code defined by the user). Thus the use of this in B::B() as a pointer to a C object fails thus when the dynamic_cast<> is called on this it fails to do what you want it to because of UB.
12.7 Construction and destruction [class.cdtor]
Paragraph 3
To explicitly or implicitly convert a pointer (a glvalue) referring to an object of class X to a pointer (reference) to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior. To form a pointer to (or access the value of) a direct non-static member of an object obj, the construction of obj shall have started and its destruction shall not have completed, otherwise the computation of the pointer value (or accessing the member value) results in undefined behavior.
[ Example:
struct A { };
struct B : virtual A { };
struct C : B { };
struct D : virtual A { D(A*); };
struct X { X(A*); };
struct E : C, D, X
{
E() : D(this), // undefined: upcast from E* to A*
// might use path E* → D* → A*
// but D is not constructed
// D((C*)this),
// defined:
// E* → C* defined because E() has started
// and C* → A* defined because
// C fully constructed
X(this) { // defined: upon construction of X,
// C/B/D/A sublattice is fully constructed
}
};
— end example ]
</quote>
Each base class constructor is executed before the derived class constructor, and during the B constructor, the dynamic type of the object is B; it does not become a C until you enter the C constructor. So you cannot do anything that requires a dynamic type of C: you can't cross-cast to any of Cs other base classes, and if you called a virtual function, then you would not get any overrides provided by C.
Under the hood, the dynamic type is (in most implementations at least) determined by a pointer in the object (known as the "vptr"), which points to some static data specifying properties of the class, including a table of virtual functions (known as the "vtable") and the information needed for dynamic_cast and typeid. Before each constructor, this is updated to point to the information for the class currently under construction.
During the construction of A then the dynamic type is A regardless. This is because you would start calling member functions of derived classes and accessing derived member variables before it's been constructed, which would be UB and very bad.
Since B doesn't inherit from A (B is parent-most class), the dynamic type of B during its constructor is B. Only when both the A and B parents are constructed can the child C be constructed, allowing for sideways dynamic_casting.
It doesn't work inside B, because B doesn't inherit from A