I believe It's C++ but it may be C? Its been a while since I looked at C++ so I didn't end up finding the bug. I was asked this question in an interview and didn't have an answer. I obviously didn't get the job but now I'm curious to find the answer.
#include<iostream>
class Base {
public:
Base() {
std::cerr<<"constructing Base " << this << std::endl;
i = new int;
}
~Base() {
std::cerr<<"destroying Base " << this << std::endl;
delete i;
}
private
int* i;
};
class Derived : public Base {
public:
Derived() {
std::cerr<<"constructing Dervied " << this << std::endl;
d = new double;
}
~Derived() {
std::cerr<<"destroying Derived " << this << std::endl;
delete d;
}
private
double* d;
};
int main(int argc, char** argv) {
using namespace std;
int ret = 1;
Base* thePtr = new Derived;
delete thePtr;
return ret;
}
Thanks in advance!
The Base's destructor is not virtual. This will leak the Derived::d pointer, at best.
Actually, it's undefined behaviour to delete a pointer of derived class through a base class pointer without virtual destructor (thanks to GManNickG)
You should mark Base class destructor as virtual. In your code Base class destructor isn't virtual & base class pointer is pointing to Derived class object so by default destructor will be called of Base class unless it is marked as virtual. This will leak dynamically allocated memory of Derived class data member d & invokes undefined behaviour.
Let me make a list...
Base destructor is not virtual.
It's using raw pointers instead of smart pointers
In fact, it doesn't need pointers, new and delete at all
And therefore, it doesn't actually need destructors.
Also Derived is spelled Dervied
... and it won't compile.
Your base class destructor should be declared virtual, it looks like
cpp
virtual ~Base()
{
std::cerr<<"destroying Base " << this << std::endl;
delete i;
}
you can find it here http://www.programmerinterview.com/index.php/c-cplusplus/virtual-destructors/
Virtual destructors are useful when you can delete an instance of a derived class through a pointer to base class:
there is a major problem with the code above: the destructor for the "Derive" class does not get called at all when we delete basePtr,
The construction of derived object follow the construction rule but when we delete the "b" pointer(base pointer) we have found that only the base destructor is call.But this must not be happened.
To do the appropriate thing we have to make the base destructor virtual. it's similar to the run-time polymorphism, so that the compiler will find this is a base pointer but point to the derived class, and automatically bound derived class destructor for it
Related
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.
Base class
class Base
{
public:
Base()=default;
virtual void f(){cout << "base class\n";}
virtual ~Base(){}
};
Derived class
class Derive : public Base
{
public:
Derive()=default;
void f() override {cout << "derived class\n";}
};
main function
int main()
{
Base* bsd = new Base;
Derive* dru = new Derive;
if(Derive* drd=dynamic_cast<Derive*>(bsd)){
drd->f();
cout << "downcast successful\n";
}else{
cout << "downcast failed\n";
}
if(Base* bsu=dynamic_cast<Base*>(dru)){
bsu->f();
cout << "upcast successful\n";
}else{
cout << "upcast failed\n";
}
delete bsd;
delete dru;
}
It turns out upcast works fine, while downcast failed. It seems makes sense that way. If the derived class contain member objects that are not declared in the base class and do not have default constructors, what gonna happen during downcast?
Besides, the target pointer *drd(*bsd) created through dynamic_cast points to the same object with the pointer *bsu(*dru) to be cast. So delete once will be enough. We will not have a dangling pointer, right?
Casting does not create anything new or changes objects in any way. Casting changes the interpretation of an existing object, so if an object is not a Derive, there is no way to make it a Derive through casting.
Note that Derive is also a Base, because inheriting creates an "is a" relationship. That's why upcasting works: all it does is telling the compiler that it should treat the pointed to object as Base, even though the object is actually Derive.
It makes no difference in your program, so here is an illustration of what the cast does:
class Derive : public Base
{
public:
Derive()=default;
void f() override {cout << "derived class\n";}
void added() {cout << "hello" << endl; }
};
dru->added(); // Works
Base* bsu=dynamic_cast<Base*>(dru);
bsu->added(); // Does not compile
Essentially, the cast "hides" added interfaces from the compiler, ordering it to treat a Derive object as if it were Base. Of course, the overrides continue to be called correctly, because the overriden member-functions are part of the Base's interface.
I'm not sure I got your question right, but yes, you can safely delete a derived class using a base pointer, if the destructor is virtual.
A "dangling pointer" is something different: After you have deleted bsd and dru, you can not use these pointers anymore, they have become dangling pointers, because they point to deleted memory that you don't own anymore.
dynamic_cast checks run-time type check and provides a safe conversion. If a meaningful conversion is possible then you'd get a valid object after downcast. As you pointed out, in your example the underlying object was of "base class". Run time type check failed. However if the underlying object was of "derieved class" dynamic_cast would have succeeded. ex:
class CBase
{
public:
CBase(void);
virtual ~CBase(void);
virtual void identify()
{
std::cout << "base class" << std::endl;
}
};
class CDerieved : public CBase
{
public:
CDerieved(void);
virtual ~CDerieved(void);
virtual void identify()
{
std::cout << "Derieved class" << std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
CDerieved* pderieved = new CDerieved;
pderieved->identify();
CBase* pb = static_cast<CBase*>(pderieved);
pb->identify();
CDerieved* pd1 = dynamic_cast<CDerieved*>(pb);
pd1->identify();
return 0;
}
The above code will succeed.
But please remember, if you find needing a downcast, then the design needs to be revised.
In the code below, why is the ~Derived() destructor called automatically?
#include<iostream>
using namespace std;
class Base
{
public:
virtual ~Base()
{
cout << "Calling ~Base()" << endl;
}
};
class Derived: public Base
{
private:
int* m_pnArray;
public:
Derived(int nLength)
{
m_pnArray = new int[nLength];
}
virtual ~Derived()
{
cout << "Calling ~Derived()" << endl;
delete[] m_pnArray;
}
};
int main()
{
Derived *pDerived = new Derived(5);
Base *pBase = pDerived;
delete pBase;
return 0;
}
Because your base class destructor is virtual
virtual ~Base();
the call to delete on a pointer to a base class results in virtual call to destructor and as any virtual call is dispatched to matching function in derived class. It is not only good, but necessary: otherwise the behavior is undefined.
This is crucial for a derived classes which destructor is not an empty function. Non-virtual call would otherwise result in calling base class destructor, derived resources being leaked, etc.
When you have at least one virtual function in a class, then the compiler creates a single table for the class listing the member function pointers. Consider:
struct Base
{
virtual ~Base() { };
int n_;
};
In pseudo-code you can imagine the compiler adding:
void* Base::__virtual_dispatch_table[] = { (void*)&Base::~Base };
Then, when you have an actual object of type Base it will have an extra hidden data member that points to the Base::__virtual_dispatch_table (the "VDT"):
Variable definition Memory layout
------------------- -------------
Base myBase; int n_;
void** __p_vdt = Base::__virtual_dispatch_table;
Now, if you have a Base* p and delete p;, the compiler says "hey - it's virtual - I won't hardcode a call to Base::~Base, instead I'll generate code that does something like this pseudo-code:
void (Base::*p_destructor) = p->__p_vdt[0]
*p_destructor(p); // "p" will provide the "this" value while the destructor runs
Why would you want to do all that? Because when you come along with a Derived object...
class Derived: public Base
{
private:
int* m_pnArray;
...
...the compiler can create a separate virtual dispatch table...
void* Derived::__virtual_dispatch_table[] = { (void*)&Derived::~Derived };
...andd lay out the Derived object's memory like this:
Variable definition Memory layout
------------------- -------------
Derived derived; int n_;
void** __p_vdt = Derived::__virtual_dispatch_table;
int* m_pnArray;
Notice that the __p_vdt is in the same relative location within the object layout, but now points to the Derived class's virtual dispatch table?
Now, if you create a Base* to derived, the exact same code needed to call the destructor for a Base object, which - in case you've lost track - was...
void (Base::*p_destructor) = p->__p_vdt[0]
*p_destructor(p); // "p" will provide the "this" value while the destructor runs
...can be run but will end up using the Derived object's __p_vdt value of Derived::__virtual_dispatch_table, and finding the Derived class's destructor.
Because it allows you to treat any Base object (which may in fact be a Derived) as an object that you can delete.
In this case, if delete pBase didn't call the Derived destructor, the data held by m_pnArray would never get deleted, i.e. a "memory leak" would occur.
When you call
delete pBase;
It looks at the virtual function table of pBase to find the appropriate destructor to begin unwinding at, and it finds Derived::~Derived and then works its way down the stack.
#include <iostream>
using namespace std;
int *p1;
struct base
{
base():a(10){}
int a;
~base()
{
cout << "~base()\n";
}
};
struct derive:public base
{
derive():b(5){
p1=&b;
cout << p1 << "\n";
}
int b;
~derive()
{
cout << "~derive()\n";
}
};
int main()
{
base *p = new derive;
delete(p);
cout << *p1;
return 0;
}
I think b of derive shoule not be deleted,but what p1 pointed was deleted.
how it works when I delete pointer of a base class that was pointed to an derive class when no dynamic inherit occured?
sorry for my english.
It's undefined behavior, because the base class destructor is not virtual. UB means anything can happen.
Don't delete a derived object through a base class if the base class doesn't have a virtual destructor.
What one would expect with following code
base *p = new derive;
delete(p);
is that the derive object pointed to by p is cleaned up correctly.
This can only be done when the base class has a virtual destructor. This is not the case here, so whatever cleanup that happens in the derive destructor will not happen, which is an unhealthy situation (or as put more technically by Luchian: undefined behavior).
I am not 100% I understand the question, and it has been awhile since ive used c++, but I will give it a shot.
Generally if you have an object that is going to be allocated dynamically you need to have 3 things, that my teachers used to call "The trilogy of evil".
Copy Constructor
Assignment Operator
Virtual Destructor
the first two have to do with making "deep copies" while the third is what we are concerned with.
You need to mark your destructor virtual to add it to the objects virtual table, that way the destructors will call up a chain.
so:
class A
{
public A(A & copy) { ... }
public static A & operator = (A & rhs) { ... }
virtual ~A() { ... }
}
class B : A { } // inherits A's destructor via virtual table
class A {
public:
A() { }
~A() { cout << "A Destructor \n" ; }
};
class B :public A{
public:
B() { }
virtual ~B() { cout << "B Destructor \n" ; }
};
class C : public B {
public:
C() { }
~C() { cout << "C Destructor \n"; }
};
int main()
{
A *pointA = new A;
A *pointB = new B;
A *pointC = new C;
delete pointA;
delete pointB;
delete pointC;
}
It will invoke undefined behavior at the second (and third) delete, because A's destructor is not virtual.
§5.3.5/3:
if the static type of the operand is different from its dynamic type, the
static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.
If you make the destructor of A virtual, you get well-defined behavior, and the destructor of the dynamic type is called. (And each of those in turn calls the base destructor.) Your output would be:
A destructor
B destructor
A destructor
C destructor
B destructor
A destructor
For what it's worth, when you're that close to a compilable snippet, you should leave the includes. Also, just use struct instead of class to be concise about the public stuff, and leave out empty constructors.
As GMan pointed out, attempting to call the delete operator on a base pointer requires a virtual destructor for the compiler to be able to destroy subclass objects correctly. A lot of people oversimplify this to a rule like, "If a class has virtual functions, it needs a virtual destructor." That is not necessarily the case; even a base class which has no virtual functions still needs a virtual destructor if you want to allow clients to delete the class through a base pointer. If you don't, the destructor should be protected and not public.
There is an excellent book that describes this in detail and more called C++ Coding Standards by Herb Sutter. I recommend it as a starting point in your C++ adventures. :-)