Non-virtual destructor when using shared_ptr to opaque type

Viewed 50

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()
0 Answers
Related