简体   繁体   中英

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.

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 (eg 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; }
};

In general abstract classes in c++ should provide a virtual destructor definition. This can be made abstract also

 virtual ~MyClass() = 0;

 // Define a body in any case
 MyClass::~MyClass() {}

An abstract class doesn't need to declare a constructor (Which can't be a pure virtual function BTW).

A constructor may make sense, when your abstract class isn't a plain interface (no data members).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM