I'm in a situation where in I've to create an object of either type A or B depending on a condition. Both A and B inherit from the same base class, in this case - Base. The base class has a virtual protected destructor.
The variable is declared as std::unique_ptr<Base> member and then depending on the condition, I use either std::make_unique<A> or std::make_unique<B>.
I'm calling member.reset() but getting a compiler error since the destructor is declared protected. But what I don't understand is why is the compiler not able to call the correct destructor since the destructor is also declared virtual.
But is there some other way to fix this? And why exactly does the virtual destructor not help here?
Full code -
#include <iostream>
#include <memory>
using namespace std;
class Base
{
public:
Base() = default;
protected:
virtual ~Base() = default;
private:
int a;
};
class A : public Base
{
};
class B : public Base
{
};
unique_ptr<Base> get_object(bool cond) {
if (cond) {
return make_unique<A>();
} else {
return make_unique<B>();
}
}
int main()
{
cout << "Hello World";
bool cond;
cin >> cond;
unique_ptr<Base> a_or_b = get_object(cond);
a_or_b.reset();
return 0;
}
The base class has a virtual protected destructor.
I don't see any point in that. Making the destructor protected disallows destroying objects through base class pointers. This makes sense as a protection mechanism if doing so would cause undefined behavior (i.e. if the destructor is non-virtual and the most-derived type different from the base class type).
However, making the destructor virtual intents it to be called through the base class pointer in order to destruct also possibly derived class objects, contradicting the effect of protected.
std::unique_ptr is unable to destroy the object through the base class pointer because of the reason given above. virtual does not change that calling the destructor through the base class pointer is disallowed.
See also the C++ core guidelines rule C.35.
Related
Is there any point to making virtual member functions, overridden from a base class private, if those are public in the base class?
struct base {
virtual void a();
};
struct derived : base {
// ...
private:
void a() override;
};
If you are forced to do a 2-phase construction on the implementation class (i.e. have an init() method as well as or instead of a constructor that has to be called (I know, but there are reasons), then this stops you calling any /other/ methods directly on the instance pointer before you pass it back as an interface pointer. Go the extra mile, make the inheritance private, and have your one public init function return the interface pointer!
Another reason is you just don't /need/ to write public: in a final implementation class declaration, so then by default everything is private. But why you would do that and use struct instead of class I don't know. Perhaps this was converted from class at some point due to a style war?
Looking at your design, I see one cannot call derived::a directly, but only through a base interface.
Is there any point? Consider that, once we have a derived instance, we can always up-cast to its base, so given
derived d;
while d.a() wouldn't compile, we can always do
base & b = d;
b.a(); //which actually calls derived::a
In other words: derived::a is not that private, after all, and I would discourage this design, which can be confusing to the user.
Things change if the members private in derived are private in base, as well: this time it is clear that they just cannot be called directly, outside base or derived.
Let's say we have a couple of functions, and want them to be called conditionally, according to a value passed as an argument to a third one:
struct base
{
void dosomething(bool x)
{
if(x)
{
do_this();
}
else
{
do_that();
}
}
private:
virtual void do_this(){}
virtual void do_that(){}
};
Thus a derived class could be like:
struct derived : base
{
private:
void do_this() override { }
void do_that() override { }
};
and no other class can call them, unless it extended base itself:
derived d;
d.dosomething(true); //will call do_this() in derived
d.dosomething(false); //will call do_that() in derived
d.do_that() //won't compile
Yes, if you inherit the base class as private. Otherwise, it is more of a weird explicit-like restriction - user has to has to make an explicit conversion to use the function - it is generally ill advised as few will be able to comprehend the author's intention.
If you want to restrict some functions from base class, make a private/protected inheritance and via using keyword declare which base-methods you want to be protected/public in the derived class.
The same reasoning as for non-virtual methods applies: If only the class itself is supposed to call it make it private.
Consider the template method pattern:
struct base {
void foo() { a() ; b(); }
virtual void a() = 0;
virtual void b() = 0;
};
struct derived : base {
private:
void a() override {}
void b() override {}
};
int main()
{
derived().foo();
}
Perhaps a and b should have been protected, but anyhow the derived can change accesibility and it requires some documentation so that derived knows how it is supposed to implement a and b.
Given this program:
struct Base
{
virtual void f() {}
};
struct Derived:public Base
{
};
int main()
{
Derived* c = new Derived;
delete c;
}
gcc-4.4 -Wall is fine but gcc-5.2 -Wall gives warning: deleting object of polymorphic class type 'Derived' which has non-virtual destructor might cause undefined behaviour [-Wdelete-non-virtual-dtor]
I saw the discussion on deleting a base pointer but in my case it's the derived object. I think it's a gcc bug, but apparently GNU doesn't think so. Is there anyway to get rid of the warning without changing the base class definition?
GCC has every right to emit that warning. Why? Because unless you declare that Derived is final, it's entirely possible for someone to create a MoreDerived type that is derived from Derived. At which point, your deletion of a Derived pointer can very much be invalid.
Adding a virtual destructor to Base has no real downsides. Yes, the destructor will be a virtual call. But that's hardly going to be a performance bottleneck.
Adding virtual ~Base() {} to Base would fix the warning.
So would adding final to Derived.
class Derived final : public Base
{
...
};
And so would adding a virtual destructor to Derived.
class Derived : public Base
{
public:
virtual ~Derived() {}
...
};
Also, if you don't plan on using Derived polymorphically, you could inherit it privately. This doesn't prevent the warning, but you could never assign a Derived pointer to a Base pointer, and thus could never delete a Derived via a Base pointer.
class Derived : private Base
{
...
};
The C++ Standard, [expr.delete], paragraph 3 [ISO/IEC 14882-2014], states the following:
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.
Do not delete an object of derived class type through a pointer to its base class type that has a non-virtual destructor. Instead, the base class should be defined with a virtual destructor. Deleting an object through a pointer to a type without a virtual destructor results in undefined behavior.
In this compliant solution, the destructor for Base has an explicitly declared virtual destructor, ensuring that the polymorphic delete operation results in well-defined behavior.
struct Base {
virtual ~Base() = default;
virtual void f();
};
struct Derived : Base {};
void f() {
Base *b = new Derived();
// ...
delete b;
}
You can find more information, if use this link: https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP52-CPP.+Do+not+delete+a+polymorphic+object+without+a+virtual+destructor
#include<iostream>
class base
{
public:
virtual ~base(){std::cout << "base\n";}
};
class derived : public base
{
private:
~derived(){std::cout << "derived\n";} /* destructor is private */
};
int main()
{
base *pt= new derived;
delete pt;
}
The above program compiles and runs fine.
How does the derived class destructor get invoked being private ?
This will happen not only with destructors.
You can override any virtual public function with a private one.
#include<iostream>
class base
{
public:
virtual void x(){std::cout << "base\n";}
};
class derived : public base
{
private:
void x(){std::cout << "derived\n"; base::x();}
};
int main()
{
base *pt= new derived;
pt->x(); //OK
//((derived *)pt)->x(); //error: ‘virtual void derived::x()’ is private
derived *pt2= new derived;
//pt2->x(); //error: ‘virtual void derived::x()’ is private
((base *)pt2)->x(); //OK
}
Upside/downside of this is you will have to use pointer to base to access this method.
This feature is one of ways to separate public API from customized implementation.
So, in other words, your destructor is getting called because it was declared as public in base and you are calling it trough pointer to base.
That may be surprising, but if you think about it, it's perfectly logical.
What the compiler sees is that you delete a pointer-to-base, which has an accessible destructor. So it has no reason to complain about what you're doing. Incidentially, it's a virtual destructor, but that's not something to complain about either.
At runtime, the virtual destructor is invoked. Since it turns out that the pointer-to-base is really pointing at a derived object, consequently the derived constructor needs to be invoked first.
"But that one is private!" says you. That's right, however, there is no notion of public/private during runtime. So, again, there is no reason for anything bad to happen. No error is generated.
I've had some second thoughts on multiple virtual destructors, esp. after reading reading http://blogs.msdn.com/b/oldnewthing/archive/2004/05/07/127826.aspx .
Suppose I have
class Base
{
public:
Base();
virtual ~Base();
private:
Logger* _logger;
};
//and
class Derived : public Base{
public:
Derived();
virtual ~Derived();
private:
Logger* _logger;
};
in the cpp files, in each destructor I am deleting the respective _logger pointers
Base::~Base(){ //base.cpp
delete _logger;
}
Derived::~Derived(){ //derived.cpp
delete _logger;
}
will this work as I intended, without memory leaks?
First off, if you make the base class destructor virtual, all derived classes will automatically get a virtual destructor if you declare them as virtual or not. This is generally true for matching signatures: if a base class has a virtual function with the same signature as a function in a derived class, the function in the derived class is an override and is virtual (although in C++ 2011 you can prevent further overriding using the final keyword in which case another override would create an error).
That said, destructors are special: when you make a destructor virtual it will still be called even if there is another overriding destructor! The only impact of a destructor being virtual is what happens if you delete an object using a pointer to a base class when the object actually happens to be of a derived type: If the destructor isn't virtual you get undefined behavior while the Right Thing happens if the destructor is virtual. For example:
class not_a_base {};
class bad_idea: public not_a_base {};
class a_base { public: virtual ~a_base() {} };
class ok: public a_base {};
int main() {
a_base* ab = new ok;
delete ab; // <---- all is good here!
not_a_base* nab = new bad_idea;
delete nab; // <---- results in undefined behavior
}
The reason destructors are not virtual by default is simply that this would mean that object size is always increased by a word size which is unacceptable in general.
Base::_logger is a different variable then Derived::_logger. So you should delete Derived::_logger in Derived's dctor, or else you leak memory.
Note that this has nothing to with it being private. Consider this example program:
#include <iostream>
class A {
public:
bool foo;
};
class B: public A {
public:
bool foo;
};
int main()
{
B b;
std::cout << &b.B::foo << ' ' << &b.A::foo << '\n';
}
The addresses are different. That means they're different variables, even though they have the same name. This is possible since each class introduces its own namespace, so the names don't really clash. The first is A::foo, the other one B::foo.
Since your destructors are virtual, both will get called, and the correct _logger will be deleted in both of them.
Suppose I have this code
class Base{
public:
int getVal();
private:
int a, b;
};
class Derived::public Base{
public:
void printVal();
};
int main(){
Base *b = new Derived();
delete b;
}
I know a virtual destructor would delete things properly, but is it bad to delete with base pointer (when there is no virtual destructor) even if there are no virtual functions and no data members in the derived class? What will happen if this is done?
Is it bad to delete with base pointer (when there is no virtual destructor) even if there are no virtual functions and no data members in the derived class?
Yes.
The behavior is undefined regardless the contents of the derived class.
What will happen if this is done?
Anything could happen.
For primitive-type data, your example will most likely work in practice. As a matter of fact, incurring a vtable could actually hinder performance (so there may be some legitimate use here), but it is technically undefined, per 5.3-5.4:
If the static type of the operand [of
the delete operator] 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
behaviour is undefined.
It really all depends on the "heapness" of the data in your class, and as there are no heap-allocated members (in your case), you should be fine, but it's definitely a code smell.
The virtual desctructor in the derived class is needed in order to properly call the derived destructor (polymorphism), when the derived object is created through a pointer to the base class.
High Integrity CPP Rule 3.3.2 Write a 'virtual' destructor for base classes. (QACPP 2116)
Justification: If an object will ever be destroyed through a pointer to its base class, then that base class should have a virtual destructor. If the base class destructor is not virtual, only the destructor for the base class will be invoked. In most cases, destructors should be virtual, because maintenance or reuse may add derived classes that require a virtual destructor.
class Base {};
class Derived : public Base { public: ~Derived() {} };
void foo() {
Derived* d = new Derived; delete d; // correctly calls derived destructor
}
void boo() {
Derived* d = new Derived; Base* b = d; delete b; // problem! does not call derived destructor!
}