简体   繁体   中英

Prevent child class from being instantiated

Is it possible? What i need is to prevent all child classes from being instantiated, so that they can not be created via constructors. Here is the example.

class Parent { }
class Child : public Parent {
    Child();
    ~Child();
}
Child* c = new Child(); // --> i need to fight with this
Child* p = factory->CreateClass<Child>(); // --> only this should work

The idea is to provide a type that can only be instantiated by the factory and to expect this type as an argument in every constructor of the base class.

This way, the constructors of the derived classes have to pass this argument to the constructor of the base class. Since they cannot instantiate this argument by themselves, the only way is to expect this argument too, thus these constructors can only be called from the factory.

Note that, in this example, I suppose that you use a hierarchy of types in order to achieve dynamic polymorphism; this is absolutely not mandatory. If you are not interested in dynamic polymorphism, the factory function should have a result with type std::unique_ptr<ChildType> .

/**
  g++ -std=c++17 -o prog_cpp prog_cpp.cpp \
      -pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
      -g -O0 -UNDEBUG -fsanitize=address,undefined
**/

#include <memory> // std::unique_ptr

// #include <type_traits> // std::is_base_of_v

#include <iostream>
#include <string>
#include <vector>

class Parent
{
public:
  // ensure correct destruction of derived instances
  virtual ~Parent() =default;

  // forbid copy/move in order to prevent from slicing
  Parent & operator=(const Parent &) =delete;
  Parent(const Parent &) =delete;
  Parent & operator=(Parent &&) =delete;
  Parent(Parent &&) =delete;

  template<typename ChildType>
  static
  std::unique_ptr<Parent> // the actual type can be Parent or a derived type
  create_instance()
  {
    // this check is redundant because FactoryTag in only
    // accessible to types derived from Parent
    // static_assert(std::is_base_of_v<Parent, ChildType>);
#if 1
    return std::unique_ptr<Parent>{new ChildType{FactoryTag{}}};
#else
    // contructor is inaccessible to std::make_unique()
    return std::make_unique<ChildType>(FactoryTag{});
#endif
  }

  virtual
  std::string
  show() const
  {
    return std::to_string(i_);
  }

protected:
  class FactoryTag
  {
    friend class Parent; // only this class can instantiate this tag
    FactoryTag() {};
  };

  Parent(FactoryTag) // a tag is mandatory for instantiation
  : i_{1}
  {
    // nothing more to be done
  }

private:
  int i_;
};

class Child: public Parent
{
public:

  Child(FactoryTag tag)
  : Parent{tag}
  , j_{2.3}
  {
    // nothing more to be done
  }

/*
  Child()
  : Parent{} // FactoryTag is expected
  , j_{4.5}
  {
    // nothing more to be done
  }

  Child()
  : Parent{FactoryTag{}} // constructor of FactoryTag is private
  , j_{6.7}
  {
    // nothing more to be done
  }
*/

  std::string
  show() const override
  {
    return Parent::show()+' '+std::to_string(j_);
  }

private:
  double j_;
};

int
main()
{
  const auto p=Parent::create_instance<Parent>();
  const auto c=Parent::create_instance<Child>();
  std::cout << p->show() << '\n'; // displays    1
  std::cout << c->show() << '\n'; // displays    1 2.300000
  auto v=std::vector<std::unique_ptr<Parent>>{};
  v.emplace_back(Parent::create_instance<Parent>());
  v.emplace_back(Parent::create_instance<Child>());
  for(const auto &e: v)
  {
    std::cout << e->show() << '\n'; // displays    1
  }                                 // then        1 2.300000
  return 0;
}

And, if the Factory needs to be a complex object separated from Parent (not just a simple static function), you can just state friend class Factory; in Parent and in FactoryTag . Then you can remove create_instance() from Parent and provide something like this:

class Factory
{
public:
  template<typename ChildType>
  std::unique_ptr<Parent> // the actual type can be Parent or a derived type
  create_instance()
  {
    // this check is redundant because FactoryTag in only
    // accessible to types derived from Parent
    // static_assert(std::is_base_of_v<Parent, ChildType>);
#if 1
    return std::unique_ptr<Parent>{new ChildType{Parent::FactoryTag{}}};
#else
    // contructor is inaccessible to std::make_unique()
    return std::make_unique<ChildType>(Parent::FactoryTag{});
#endif
  }
};

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