4

I am trying to understand virtual destructors. The following is a copy paste from this page When to use virtual destructors?

Here, you'll notice that I didn't declare Base's destructor to be virtual. Now, let's have a look at the following snippet:

Base *b = new Derived(); // use b 
delete b; // Here's the problem!

[...] If you want to prevent the deletion of an instance through a base class pointer, you can make the base class destructor protected and non-virtual; by doing so, the compiler won't let you call delete on a base class pointer.

I don't understand why the deletion is prevented by having a protected non-virtual base class destructor. Doesn't the compiler think that we're trying to call delete from a base class object? What does protected have to do with that?

Community
  • 1
  • 1
usual me
  • 7,130
  • 6
  • 40
  • 81

3 Answers3

11

The C++ Standard has this to say about delete (section 5.3.5p10):

Access and ambiguity control are done for both the deallocation function and the destructor (12.4, 12.5).

Therefore, only code that has access to the destructor is able to use delete. Since the destructor is protected, that means that no one can call delete on a pointer of type Base*. Only subclasses can use the destructor at all (and the only thing that will is the subclass's own destructor, as part of the subobject destruction process).

Of course, the subclass should make its own destructor public, allowing you to delete objects through the subclass type (assuming that is the correct actual type).

NOTE: Actually, other members of Base can do delete (Base*)p; since they have access. But C++ assumes that someone using this construct will not be doing that -- C++ access control only provides guidance to code outside your class.

Ben Voigt
  • 260,885
  • 36
  • 380
  • 671
6

delete b; effectively performs b->~Base(); deallocate(b);. The first part - calling the destructor - would fail to compile if the destructor is inaccessible (in the same way that calling any other inaccessible method fails).

Igor Tandetnik
  • 45,980
  • 4
  • 51
  • 75
  • 1
    "function", please. It's not `virtual` and isn't dispatched like a method. – Ben Voigt Oct 25 '13 at 01:16
  • 1
    @BenVoigt: I don't quite see how the term "method" is more apt when applied to a virtual member function than a non-virtual one. I must admit I fail to appreciate the difference. I could understand the objection that there are no methods at all in C++, only member functions (though I'd think of it as perhaps overly pedantic). But I don't understand this particular division. – Igor Tandetnik Oct 25 '13 at 01:20
  • 1
    @BenVoigt: no less an authority than [Wikipedia](http://en.wikipedia.org/wiki/Method_(computer_programming)) defines "method" merely as "a subroutine (or procedure) associated with a class", and even talks about "virtual methods" and "non-virtual methods". I rest my case. – Igor Tandetnik Oct 25 '13 at 01:23
  • 1
    There are no methods, only member functions. But virtual member functions can be dispatched *like* a method. ("method" is defined in OOP theory, and definitely involves dispatch. Think about the expanded case of "multimethods".) – Ben Voigt Oct 25 '13 at 01:27
  • 1
    @BenVoigt: Java has static methods. Are those "dispatched"? I suspect that, even if there once was a strict definition of the term "method" along the lines you insist upon, colloquial use has departed from such definition significantly, for better or worse. – Igor Tandetnik Oct 25 '13 at 01:30
  • 1
    Java horribly abuses the term method, because somehow the language designers were afraid of the word *function*. Even by that Wikipedia definition, a Java declaration using the `static` keyword is not a method at all, since it is not associated with an object. – Ben Voigt Oct 25 '13 at 17:09
  • 1
    @BenVoigt: You are mangling the quote to fit your argument. Wikipedia says "associated with a class", not with an object. – Igor Tandetnik Oct 25 '13 at 17:16
  • Say what? Well, it wouldn't be the first time wikipedia was wrong. Oh my, that entire article is a complete disaster. Look below where they equate "static method" to "non-virtual call" and "instance method" with polymorphism. Yeah, any claim *loses* weight for using that article as evidence. – Ben Voigt Oct 25 '13 at 17:17
  • @BenVoigt: Well, what authority can you cite to support your argument? Java standard uses the term "static method"; C# standard uses the term "static method". They also talk about "final method" and "sealed method", which is the closest thing those languages have to a non-virtual member function. Which normative document, then, makes a distinction between virtual non-static "methods" and non-virtual or static "something else"? – Igor Tandetnik Oct 25 '13 at 17:22
  • @BenVoigt: Although I agree that wikipedia is not an authority and its information cannot be trusted, for many people *in C++* "method" means "member function", while "function" *strictly* means "non-member function". With *this* terminology calling destructor a "function" wouldn't make any sense, because it has access to *this* and is declared within class. Of course, OOP theory you studied might have another meaning associated with "method" and "function", but that's another story. – SigTerm Oct 25 '13 at 17:24
  • @Igor: Smalltalk, which I believe introduced the "method" terminology, defines that methods are (1) associated with objects, not types and (2) polymorphic. There are "class methods" which are also dispatched polymorphically. Plain functions were apparently considered unnecessary, and OOP purists tried to exclude them. – Ben Voigt Oct 25 '13 at 19:17
  • @BenVoigt: does Smalltalk have any kind of function-like things that are not polymorphic and/or not dispatched? If it doesn't, then the statement "all member function-like things in Java, C# **and** Smalltalk are properly referred to as methods" is true. – Igor Tandetnik Oct 25 '13 at 19:35
  • @Igor: That doesn't follow. From a brief review, Smalltalk standard uses the word function plenty, and uses "method" to refer to its own polymorphic behaviors to distinguish from functions used in procedural programming. How is a "member function-like thing" properly referred to as a method if it has none of the characteristics of a Smalltalk method except lexical scope? C# methods obviously are called that because Java did. Java used the word in order to ride the coattails of OOP, not because Java static function acted at all like Smalltalk methods. – Ben Voigt Oct 25 '13 at 21:15
  • @Igor: Anyway, it appears that Smalltalk *does* have functions which are not methods... "blocks". But they are also objects in their own right, which blurs any distinction between "code associated with an object" and code not so associated. Anyway, blocks seem to be lambdas, with closure (variable capture), and not themselves methods although they are defined inside methods. – Ben Voigt Oct 25 '13 at 21:16
  • @Igor: BTW, I would agree that member functions marked "final" or "sealed" (or in a class marked "final" or "sealed") actually qualify as methods, even though they are not dynamically dispatched. That's because the polymorphic type of the object is statically known and not because a type other than the polymorphic type is used for dispatch. If a type other that the polymorphic (aka runtime aka dynamic) type is used for dispatch then the word "method" is inappropriate. – Ben Voigt Oct 25 '13 at 21:21
0

From my understanding (based on this page), the only case we would like to use the non-virtual and protected destructor in the base class is the following:

struct unary_function {
  typedef int argument_type;
  typedef bool result_type;

protected:
  ~unary_function() {
      std::cout << "unary_function" << std::endl;
  }
};

struct IsOdd : public unary_function {
public:
  bool operator()(int number) { return (number % 2 != 0); }
};

void f(unary_function *f) {
  // compile error
  // delete f;
}

int main(int argc, char **argv) {
  // unary_function *a = new IsOdd;
  // delete a;

  IsOdd *a = new IsOdd;
  delete a;

  getchar();
  return 0;
} // main

therefore, you can only do this:

  IsOdd *a = new IsOdd;
  delete a;

or

  IsOdd c;

never these:

  unary_function *a = new IsOdd;
  delete a;

therefore, with nonvirtual protected destructor, the compiler would give an error when you try to use this

void f(unary_function *f) {
  delete f; 
  // this function couldn't get compiled because of this delete. 
  // you would have to use the derived class as the parameter 
}
randomness2077
  • 1,042
  • 2
  • 13
  • 20