Virtual Destructors
C++ allows classes to inherit from other classes.
In some cases, such as this example, this can lead to memory leaks:
class ABase {
public:
~ABase() {}
};
class A : public ABase {
std::string *value_;
public:
A() : value_(new std::string) {}
~A() { delete value_; }
};
void do_something() {
ABase *instance = new A();
//...
delete instance;
}
So here we allocate a pointer to A, assign it to “instance” which is of type ABase
, do something with it and finally delete it. It looks fine but we just leaked memory! When we called “delete instance” the code invoked the destructor ~ABase()
and NOT the destructor ~A()
. And value_
was not deleted and the memory leaked. Even if we’d used a scoped pointer to wrap value_
it would still have leaked.
The code should have said
class ABase {
public:
virtual ~ABase() {}
};
The compiler didn’t care our code was in error. It just allowed us to leak for the sake of a missing keyword.
How Rust helps
Rust also does not use inheritance so problems like ABase above cannot exist. In Rust ABase
would be declared as a trait that A implements.
trait ABase {
//...
}
struct A {
value: String,
}
impl ABase for A {
//...
}
Rust also allows our struct to implement another trait called Drop
which is equivalent to a C++ destructor.
impl Drop for A {
fn drop(&mut self) {
println!("A has been dropped!");
}
}
It allows our code to do something during destruction such as to free an open resource, log a message or whatever.