What does 'has virtual method ... but non-virtual destructor' warning mean during C++ compilation?

qazwsx picture qazwsx · Jan 6, 2012 · Viewed 45.2k times · Source
#include <iostream>
using namespace std;

class CPolygon {
  protected:
    int width, height;
  public:
    virtual int area ()
      { return (0); }
  };

class CRectangle: public CPolygon {
  public:
    int area () { return (width * height); }
  };

Has compilation warning

Class '[C@1a9e0f7' has virtual method 'area' but non-virtual destructor

How to understand this warning and how to improve the code?

[EDIT] is this version correct now? (Trying to give answer to elucidate myself with the concept)

#include <iostream>
using namespace std;

class CPolygon {
  protected:
    int width, height;
  public:
    virtual ~CPolygon(){};
    virtual int area ()
      { return (0); }
  };

class CRectangle: public CPolygon {
  public:
    int area () { return (width * height); }
    ~CRectangle(){}
  };

Answer

Bj&#246;rn Pollex picture Björn Pollex · Jan 6, 2012

If a class has a virtual method, that means you want other classes to inherit from it. These classes could be destroyed through a base-class-reference or pointer, but this would only work if the base-class has a virtual destructor. If you have a class that is supposed to be usable polymorphically, it should also be deletable polymorphically.

This question is also answered in depth here. The following is a complete example program that demonstrates the effect:

#include <iostream>

class FooBase {
public:
    ~FooBase() { std::cout << "Destructor of FooBase" << std::endl; }
};

class Foo : public FooBase {
public:
    ~Foo() { std::cout << "Destructor of Foo" << std::endl; }
};

class BarBase {
public:
    virtual ~BarBase() { std::cout << "Destructor of BarBase" << std::endl; }
};

class Bar : public BarBase {
public:
    ~Bar() { std::cout << "Destructor of Bar" << std::endl; }
};

int main() {
    FooBase * foo = new Foo;
    delete foo; // deletes only FooBase-part of Foo-object;

    BarBase * bar = new Bar;
    delete bar; // deletes complete object
}

Output:

Destructor of FooBase
Destructor of Bar
Destructor of BarBase

Note that delete bar; causes both destructors, ~Bar and ~BarBase, to be called, while delete foo; only calls ~FooBase. The latter is even undefined behavior, so that effect is not guaranteed.