Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

destructor and no constructor for abstract base class c++

I searched for this, but I didn't really understand the answers.

I am completely new to C++, and what I am trying to achieve is to have an abstract class which serves as a base class for my object types so that I can store my objects in an array of pointers of type of the abstract class instead of using void *. Moreover, my objects share a few common member functions which could easily reduce my code base with the abstract class implementation.

However, I am confused about the constructor and destructor for the abstract class.

The abstract class doesn't really need a constructor since the parameters that could be passed in that are common to both require different things to be done with said parameter in the derived class to set the protected attributes correctly (sizing of a matrix). So, is it okay to not have a constructor? Also, since I don't have a constructor what should the destructor be?

I say an answer where it was to implement a virtual destructor. However, I'm not sure what this means and there was a discussion about potential memory leaks saying there wouldn't be any as long as the derived classes reimplemented the destructor. So, this does mean that I can implement a virtual decstructor and then in the derived objects say Foo and Bar I simply implement ~Foo and ~Bar to prevent memory leaks (assuming they're correct of course)? I wasn't confident I understood what the re-implementation in the derived classes meant exactly.

like image 501
fluffy_muffin Avatar asked Nov 17 '16 23:11

fluffy_muffin


People also ask

Can an abstract class have constructor and destructor?

If the constructor for an abstract class calls a pure virtual function, either directly or indirectly, the result is undefined. However, constructors and destructors for abstract classes can call other member functions.

Can an abstract base class have a destructor?

You can create an abstract base class with only a virtual destructor.

Can abstract class have no constructor?

It is used to initialize an object. Yes, an Abstract class always has a constructor.

Can we use destructor without constructor?

Yes, the destructor is nothing more than a function. You can call it at any time. However, calling it without a matching constructor is a bad idea.


1 Answers

Destructors

In general, when implementing abstract base classes, you have two recommended options for destructors (source):

1. Implement a public, virtual destructor

Use this when you intend to have pointers of your base class, which may point to instances of the derived class. For example:

class MyBase {
public:
    virtual ~MyBase() {};
};

class MyDerived : public MyBase {
public:
    virtual ~MyDerived() {};
}

std::unique_ptr<MyBase> pInstance = std::make_unique<MyDerived>();

By making the destructor virtual in the base class (and also in the derived class), you are ensuring that the destructor for MyDerived gets called at runtime. If the destructor is non-virtual, calling delete on a pointer to MyBase will NOT call the destructor of MyDerived.

2. Implement a protected, non-virtual destructor

Use this in cases where you do not want to allow the user to create base-class pointers to your derived object.

class MyBase {
protected:
    ~MyBase() {};
};

class MyDerived : public MyBase {
public:
    ~MyDerived() {};
}

// NOT ALLOWED: std::unique_ptr tries to call protected destructor.
std::unique_ptr<MyBase> pBadInstance = std::make_unique<MyDerived>();

// Allowed: std::unique_ptr calls public MyDerived::~MyDerived()
std::unique_ptr<MyDerived> pGoodInstance = std::make_unique<MyDerived>();

This comes with an important caveat, however. If you have a deep inheritance hierarchy, having a non-virtual destructor means that you must enforce this rule all the way up your heirarchy. For example:

class MyBase {
protected:
    ~MyBase() {};
};

class MyDerived : public MyBase {
public:
    ~MyDerived() {};
}

class MyDerivedAgain : public MyDerived {
public:
    ~MyDerivedAgain() {};
}

// Uh oh! MyDerivedAgain destructor would not be called!
std::unique_ptr<MyDerived> pGoodInstance = std::make_unique<MyDerivedAgain>();

If you choose to go this route, you should ensure that you do not allow any of your base classes to be instantiated. All destructors except for leaf derived classes should be protected.

This seems a little convoluted, but it can have advantages such as avoiding vtable space, and mildly improving performance in tight loops by dodging vtable lookups at runtime (a micro-optimization at best).

Constructors

It is perfectly okay to omit the constructor in any class as long as all variables can be default-constructed (or do not have a constructor (e.g. int)). The C++ compiler will simply create the default constructor MyAbstractClass::MyAbstractClass() { }. That said, it is usually preferable to create a constructor to initialize any abstract class variables:

Okay:

class MyBase {
protected:
    int _x;
    int _y;
};

class MyDerived : public MyBase {
public:
    MyDerived(int x, int y) {
       _x = x;
       _y = y;
    }
};

Better:

class MyBase {
public:
    MyBase(int x, int y) : _x(x), _y(y) {
    }

protected:
    int _x;
    int _y;
};

class MyDerived : public MyBase {
public:
    MyDerived(int x, int y) : MyBase(x, y) {
    }
};

The "Better" version with the MyBase::MyBase(int, int) constructor is better because it forces _x and _y to be initialized immediately, and the compiler will check that the base class constructor is called. By initializing base class variables in the derived constructor, you're potentially inviting disaster because you may forget to initialize a variable and cause all sorts of runtime problems.

Appendix: A Note on "Interfaces"

If you are implementing an interface class that defines a "contract", you can skip the constructor (interfaces don't have variables or implementation, constructor is not required), and use a public virtual destructor. This ensures that any class implementing the interface is going to get cleaned up when it is deleted.

class MyInterface {
public:
    virtual ~MyInterface() = 0;

    virtual void MyMethod() = 0;

    virtual void MyOtherMethod() = 0;
};

// Base class virtual destructors should always have an implementation,
// even when they are pure-virtual.
MyInterface::~MyInterface() { }

// -----------------------------------------------------------------------------

class MyImplementation : public MyInterface {
    virtual ~MyImplementation () { }

    virtual void MyMethod() { std::cout << "MyMethod()" << std::endl; }

    virtual void MyOtherMethod()  { std::cout << "MyOtherMethod()" << std::endl; }
};
like image 148
Karl Nicoll Avatar answered Sep 25 '22 19:09

Karl Nicoll