I came across this:
struct Base {
void* operator new (size_t);
void operator delete (void*);
virtual ~Base () {} // <--- polymorphic
};
struct Derived : Base {};
void Base::operator delete (void *p)
{
Base *pB = static_cast<Base*>(p);
if(dynamic_cast<Derived*>(pB) != 0)
{ /* ... NOT reaching here ? ... */ }
free(p);
}
Now if we do,
Base *p = new Derived;
delete p;
Surprisingly, the condition inside the Base::delete is not satisfied
Am I doing anything wrong ? Or casting from void* looses the information of Derived* ?
Function operator delete is a raw-memory deallocation function. It is invoked when the actual object (that used to reside in that memory) has already been destructed. I.e. by the time you get into operator delete you object has already been wiped out. The memory the pointer points to is essentially "raw", it no longer contains an object. Trying to use any polymorphic functionality on this raw memory is useless - it will not work.
In more formal terms, according to the language standard the lifetime of an object with non-trivial destructor ends once its destructor starts. In your case all destructors have already done their work. The lifetime of the object is already over, while dynamic_cast requires a "live" object.
P.S. Formally, it is permissible to use dynamic_cast in a destructor as long as some conditions are met (see 12.7/5), but when all destructors are finished (as in your case), dynamic_cast is no longer usable.
Once your operator delete overload gets the pointer, the pointed-to object has been destroyed (the ~Derived() destructor has already been called).
You can't treat it like a Base or Derived object at all anymore after it is destroyed because it isn't a Base or Derived object anymore.
As already mentioned by the other two answers, the type of an object changes as the destructors are being executed. Once a destructor completes, the object of that type does no longer exist, and only it's base subobjects exists (until their destructors complete).
The reason for this answer is proposing an interesting experiment, what will the output of this code be? (Oh, well, all three answers already told you, but the experiment is interesting in itself):
#include <iostream>
struct base {
static void print_type( base const & b ) { // [1]
std::cout << b.type() << std::endl;
}
virtual std::string type() const { // [2]
return "base";
}
virtual ~base() { print_type( *this ); }
base() { print_type( *this ); }
};
struct derived : base {
std::string type() const {
return "derived";
}
~derived() { print_type( *this ); }
derived() { print_type( *this ); }
};
struct most_derived : derived {
std::string type() const {
return "most_derived";
}
~most_derived() { print_type( *this ); }
most_derived() { print_type( *this ); }
};
int main() {
most_derived md;
base::print_type( md );
}
Notes:
For extra fun, calls to print_type are also added in the constructor. The function serves as verification of the dynamic type of the object at that particular point in time. The function print_type (that could be a freestanding function, and implemented in a different translation unit --as to avoid the compiler from seeing inside it). While compiling the function, the compiler cannot know whether it is called from inside a constructor, destructor, or outside of any of them, so the generated code must use the dynamic dispatch mechanism, and will be dispatched to the final overrider at each point in time.
As to the validity of the code is guaranteed by §12.7/2:
To explicitly or implicitly convert a pointer (an lvalue) 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 nonstatic 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.
Conversions to base& on the call to print_type are valid as they are performed after the construction of each object has started, and before the destruction of each object has completed (each refers to each one of the subobjects of most_derived in the program).
Related
I saw a presentation on cppcon of Piotr Padlewski saying that the following is undefined behaviour:
int test(Base* a){
int sum = 0;
sum += a->foo();
sum += a->foo();
return sum;
}
int Base::foo(){
new (this) Derived;
return 1;
}
Note: Assume sizeof(Base) == sizeof(Derived) and foo is virtual.
Obviously this is bad, but I'm interested in WHY it is UB. I do understand the UB on accessing a realloced pointer but he says, that this is the same.
Related questions: Is `new (this) MyClass();` undefined behaviour after directly calling the destructor? where it says "ok if no exceptions"
Is it valid to directly call a (virtual) destructor? Where it says new (this) MyClass(); results in UB. (contrary to the above question)
C++ Is constructing object twice using placement new undefined behaviour? it says:
A program may end the lifetime of any object by reusing the storage
which the object occupies or by explicitly calling the destructor for
an object of a class type with a non-trivial destructor. For an object
of a class type with a non-trivial destructor, the program is not
required to call the destructor explicitly before the storage which
the object occupies is reused or released; however, if there is no
explicit call to the destructor or if a delete-expression (5.3.5) is
not used to release the storage, the destructor shall not be
implicitly called and any program that depends on the side effects
produced by the destructor has undefined behavior.
which again sounds like it is ok.
I found another description of the placement new in Placement new and assignment of class with const member
If, after the lifetime of an object has ended and before the storage
which the object occupied is reused or released, a new object is
created at the storage location which the original object occupied, a
pointer that pointed to the original object, a reference that referred
to the original object, or the name of the original object will
automatically refer to the new object and, once the lifetime of the
new object has started, can be used to manipulate the new object, if:
the storage for the new object exactly overlays the storage location which the original object occupied, and
the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is
const-qualified or a reference type, and
the original object was a most derived object of type T and the new object is a most derived object of type T (that is, they are not
base class subobjects).
This seems to explain the UB. But is really true?
Doesn't this mean, that I could not have a std::vector<Base>? Because I assume due to its pre-allocation std::vector must rely on placement-news and explicit ctors. And point 4 requires it to be the most-derived type which Base clearly isn't.
I believe Elizabeth Barret Browning said it best. Let me count the ways.
If Base isn't trivially destructible, we're failing to cleanup resources.
If sizeof(Derived) is larger than the size of the dynamic type of this, we're going to clobber other memory.
If Base isn't the first subobject of Derived, then the storage for the new object won't exactly overlay the original storage, and you'd also end up clobbering other memory.
If Derived is just a different type from the initial dynamic type, even if it's the same size, than the object that we're calling foo() on cannot be used to refer to the new object. The same is true if any of the members of Base or Derived are const qualified or are references. You'd need to std::launder any external pointers/references.
However, if sizeof(Base) == sizeof(Derived), and Derived is trivially destructible, Base is the first subobject of Derived, and you only actually have Derived objects... this is fine.
Regarding your question
...Because I assume due to its pre-allocation std::vector must rely on
placement-news and explicit ctors. And point 4 requires it to be the
most-derived type which Base clearly isn't. And point 4 requires it to
be the most-derived type which Base clearly isn't.
, I think the misunderstanding comes from the term "most derived object" or "most derived type":
The "most derived type" of an object of class type is the class with which the object was instantiated, regardless of whether this class has further subclasses or not. Consider the following program:
struct A {
virtual void foo() { cout << "A" << endl; };
};
struct B : public A {
virtual void foo() { cout << "B" << endl; };
};
struct C : public B {
virtual void foo() { cout << "C" << endl; };
};
int main() {
B b; // b is-a B, but it also is-an A (referred to as a base object of b).
// The most derived class of b is, however, B, and not A and not C.
}
When you now create a vector<B>, then the elements of this vector will be instances of class B, and so the most derived type of the elements will always be B, and not C (or Derived) in your case.
Hope this brings some light in.
I am learning static_cast<Type>(object) in C++. I found it calls copy constructor of Type with object. Is is true? If it is true, why it copies it? I thought it just change how to use the memory pointed by object. If it needs constructing copy, static_cast has more cost than I thought. Do I need to care of the cost?
The followings are the test code,
#include <iostream>
class Base {
public:
Base() {};
Base(const Base& org) {
std::cout << "Base Copy Constructor" << std::endl;
};
virtual ~Base() {};
};
class Derived : public Base {
public:
void static_casting(void) {
static_cast<Base>(*this);
}
};
void test_static_cast_copy_constructor(void) {
Derived a;
a.static_casting();
}
Thank you very much.
It sounds like you're expecting static_cast to work like it does with pointers, but there is no pointer casting happening in your code. static_cast<Type *>(&object) would yield a pointer to Type, and dereferencing it would indeed let you treat object as though it were of type Type (I assume this is what you mean when you say "change how to use the memory pointed by object"). This is possible because object still exists in its original form somewhere in the memory, and the pointer in effect only provides access to a portion of object's data and behaviour.
However, as pointed out in the comments, static_cast<Type>(object) yields a new object of type Type - and since a new object is created, the appropriate constructor is called, with the portions of the copied object not contained in aType object chucked out (sliced).
This question already has answers here:
Is it allowed to write an instance of Derived over an instance of Base?
(4 answers)
Closed 8 years ago.
#include <cstdlib>
struct B {
virtual void f();
void mutate();
virtual ~B();
};
struct D1 : B { void f(); };
struct D2 : B { void f(); };
void B::mutate() {
new (this) D2; // reuses storage — ends the lifetime of *this
f(); // undefined behavior - WHY????
... = this; // OK, this points to valid memory
}
I need to be explained why f() invokation has UB? new (this) D2; reuses storage, but it also call a constructor for D2 and since starts lifetime of a new object. In that case f() equals to this -> f(). That is we just call f() member function of D2. Who knows why it is UB?
The standard shows this example § 3.8 67 N3690:
struct C {
int i;
void f();
const C& operator=( const C& );
};
const C& C::operator=( const C& other) {
if ( this != &other ) {
this->~C(); // lifetime of *this ends
new (this) C(other); // new object of type C created
f(); // well-defined
}
return *this;
}
C c1;
C c2;
c1 = c2; // well-defined
c1.f(); // well-defined; c1 refers to a new object of type C
Notice that this example is terminating the lifetime of the object before constructing the new object in-place (compare to your code, which does not call the destructor).
But even if you did, the standard also says:
If, after the lifetime of an object has ended and before the storage
which the object occupied is reused or released, a new object is
created at the storage location which the original object occupied, a
pointer that pointed to the original object, a reference that referred
to the original object, or the name of the original object will
automatically refer to the new object and, once the lifetime of the
new object has started, can be used to manipulate the new object, if:
— the storage for the new object exactly overlays the storage location
which the original object occupied, and — the new object is of the
same type as the original object (ignoring the top-level
cv-qualifiers), and
— the type of the original object is not
const-qualified, and, if a class type, does not contain any non-static
data member whose type is const-qualified or a reference type, and
— the original object was a most derived object (1.8) of type T and the
new object is a most derived object of type T (that is, they are not
base class subobjects).
notice the 'and' words, the above conditions must all be fulfilled.
Since you're not fulfilling all the conditions (you have a derived object in-placed into the memory space of a base class object), you have undefined behavior when referencing stuff with an implicit or explicit use of this pointer.
Depending on the compiler implementation this might or might now blow because a base class virtual object reserves some space for the vtable, in-place constructing an object of a derived type which overrides some of the virtual functions means the vtable might be different, put alignment issues and other low-level internals and you'll have that a simple sizeof won't suffice to determine if your code is right or not.
This construct is very interesting:
The placement-new is not guaranteed to call the destructor of the object. So this code will not properly ensure end of life of the object.
So in principle you should call the destructor before reusing the object. But then you would continue to execute a member function of an object that is dead. According to standard section.9.3.1/2 If a non-static member function of a class X is called for an object that is not of type X, or of a type derived from X, the behavior is undefined.
If you don't explicitely delete your object, as you do in your code, you then recreate a new object (constructing a second B without destoying the first one, then D2 ot top of this new B).
When the creation of your new object is finished, the identity of your current object has in fact changed while executing the function. You cannot be sure if the pointer to the virtual function that will be called was read before your placement-new (thus the old pointer to D1::f) or after (thus D2::f).
By the way, it's exactly for this reason, that there are some constraints about what you can or can't do in a union, where a same memory place is shared for different active objects (see Point 9.5/2 and perticularly point 9.5/4 in the standard).
On the last line of a destructor, I have a diagnostic type message which takes a printf-like form:
"object destroyed at %p", this
I have concerns though about how well this is defined at such a point.
Should I have such reservations? Is the behaviour well-defined?
According to the C++ Standard (12.4 Destructors)
8 After executing the body of the destructor and destroying any
automatic objects allocated within the body, a destructor for class
X calls the destructors for X’s direct non-variant non-static data
members, the destructors for X’s direct base classes and, if X is the
type of the most derived class (12.6.2), its destructor calls the
destructors for X’s virtual base classes.
So your code is well-formed. All destructors of non-static data members and base classes are called after executing the body of the destructor.
Well, the pointer itself certainly still exists (it's just an address, after all). There should be no problem to print the pointer value.
On the other hand, everything that you did in the destructor has already happened. Attributes may already have been delete'd, etc, so you have to avoid anything that accesses those.
This has perfectly well defined behaviour. Consider that the this pointer can be used implicitly or explicitly throughout the destructor, e.g. whenever you access a member variable for things like delete ptr_;. After the destructor returns, the members are destroyed in reverse order of declaration/creation then the base destructors invoked.
As you might now, you can access your class members from the destructor. This would not be working if the this pointer was invalid. So you can safely assume that the address this points to is still the same that you might have printed in the constructor.
Inside the destructor the this pointer is well defined, as are all the members and bases (that will be destroyed in construction reverse order after the destructor return). So printing the address it refers is not UB.
The only thing is that the object itself cannot be assumed anymore as "polymorphic", since the derived components had already been destroyed.
class A
{
public:
virtual void fn() { std::cout << "A::fn" << std::endl; }
virtual ~A() { fn(); } //< will call A::fn(), even if B is destroying
};
class B: public A
{
public:
virtual void fn() { std::cout << "B::fn" << std::endl; }
virtual ~B() {}
};
int main()
{
B b;
A& a = b;
a.fn(); //< will print B::fn(), being A::fn virtual and being B the runtime-type of the a's referred object
return 0; //< will print A::fn() from b's A's component destructor
}
I have the following code:
struct Message
{
explicit Message(const std::string& message) : selfMessage(message) {};
~Message() { std::cerr << "Message: " << selfMessage << std::endl; }
const std::string selfMessage;
};
struct Foo
{
Foo() : fooMessage("Foo") {}
/// Destructor here is left intentionally non-virtual !!!
~Foo() { std::cerr << "~Foo" << std::endl; }
Message fooMessage;
};
struct Bar : Foo
{
Bar() : barMessage("Bar") {}
~Bar() { std::cerr << "~Bar" << std::endl; }
Message barMessage;
};
int main()
{
std::auto_ptr<Foo> foo(new Bar);
}
I expect the following output:
Message: Bar
Message: Foo
~Foo()
But actually (the code is compiled with gcc) does not print Message: Bar so as far as I understand barMessage is not destructed correctly. Why?
AFAIK non virtual d-tor affects only on the call of dtor of derived class - it will be never called, but what about stack-allocated members of derived class?
Thank you,
P.S. I already know about deprecated std::auto_ptr<>() usage :)
If you call delete on a derived object through a pointer to a base class object then you get undefined behavior unless the destructor of the base class is declared virtual.
Letting an std::auto_ptr templated on a base class go out of scope when it owns a derived class object has the effect of calling delete on a base class pointer type when the actual object is of a derived class type.
Ultimately, auto_ptr calls delete myPtr (where myPtr is a member
of type T*). Invoking delete where the static type and the dynamic
type are not the same is undefined behavior. It's not just a case where
the derived class destructors will not be called; it's a case where just
about anything can happen. And does, in cases of more complex
inheritance hierarchies.
This is only relevant for dynamically allocated instances. Invoking
delete on something which wasn't dynamically allocated is undefined
behavior (and will typically cause all sorts of problems). And except
for delete, the destructor is called on an object, not a pointer, so the
static type and the dynamic type are identical.
Bar::barMessage is not destructed precisely because the destructos of the base is not virtual. The pointer is of type Foo, and on exiting of the scope, the std::auto_ptr dill call delete on the internal pointer, which is Undefined behavior, and in this case will destroy only the Foo subobject.
Note that there is no such thing as stack allocated members of Bar, there are members with automatic storage, but in this case as the whole object has been dynamically allocated, that is not in the stack but in the heap (C++ technically has no concept of stack/heap but understand that all the Bar object is dynamically allocated)