3

In the code below, Base is an abstract base class with non-virtual destructor.

Derived, a concrete type, can only be instantiated inside the make_thing() factory function (it's declaration and definition are visible exclusively from within the scope of make_thing()).

My understanding is that, since std::shared_ptr<T> stores the concrete deleter at the point of construction, it is well defined to delete Derived via std::shared_ptr<Base>.

This would not compile if make_thing() returned a std::unique_ptr<Base> and it would be undefined behavior if it returned Base*.

While the code works as expected, I wonder whether it contains Undefined Behavior.

#include <iostream>
#include <memory>

class Base {
 public:
  void method() const noexcept { return this->do_method(); }

 protected:
  ~Base() noexcept { std::cout << "~Base()\n"; }

 private:
  virtual void do_method() const noexcept = 0;
};

std::shared_ptr<Base> make_thing() {
  class Derived final : public Base {
   public:
    ~Derived() noexcept { std::cout << "~Derived()\n"; }

   private:
    void do_method() const noexcept override {
      std::cout << "Derived::do_method()\n";
    }
  };

  return std::make_shared<Derived>();
};

int main() {
  // only way to instantiate Derived
  auto thing = make_thing();
  thing->method();
}

Sample run:

$ clang++ example.cpp -std=c++20
$ ./a.out
Derived::do_method()
~Derived()
~Base()
Escualo
  • 40,844
  • 23
  • 87
  • 135
  • `do_method` being private in the base class looks suspiscious – Den-Jason Nov 05 '21 at 14:45
  • 2
    @Den-Jason, except it isn't. Standard issue non-virtual interface pattern. – StoryTeller - Unslander Monica Nov 05 '21 at 14:47
  • @Den-Jason: provided it is called from a public function I can see no problem. – Serge Ballesta Nov 05 '21 at 14:47
  • Personally, if I already have one virtual function, making the destructor virtual has a very small marginal cost. At the very least it keeps the zealots off of you in code review. – StoryTeller - Unslander Monica Nov 05 '21 at 14:48
  • 2
    @Den-Jason: See [Non-virtual interface design pattern in C#/C++](https://stackoverflow.com/q/6481260/10077), for example. – Fred Larson Nov 05 '21 at 14:48
  • 2
    Ah, thanks guys; Herb Sutter explains it well. I've always presumed you need to make virtual methods at least protected.....http://www.gotw.ca/publications/mill18.htm – Den-Jason Nov 05 '21 at 14:51
  • 1
    @Den-Jason • for small programs, the NVI pattern is overkill. For larger programs, it is very useful. Separation of concerns from the public facing API from the inheritance facing API. – Eljay Nov 05 '21 at 14:53
  • Question is definitely a duplicate — apologies. Not only is it a near duplicate to the indicated question, but also to a question I myself asked seven years ago! I must be getting old… – Escualo Nov 05 '21 at 15:07

0 Answers0