0

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
Nick Bane
  • 21
  • 7
  • Make the constructor `private` and provide a friend function maybe? Eg: https://stackoverflow.com/a/6568502 – Rahul Bharadwaj Feb 07 '21 at 10:49
  • Assuming `Child` is not an abstract class (which cannot be instantiated by ANY code at all) make all constructors `private` or `delete`d (or both). Also make the destructor `private` (since, if construction fails with an exception thrown, the destructor will be called, and needs to be accessible to the code that attempts to create the object). If there is a need to be able to instantiate the object in some specific way, it will then be necessary for `Child` to either have a `friend` (class or function) OR a `public` and `static` member function that can instantiate the object. – Peter Feb 07 '21 at 12:11
  • Is factory yet another type, or could CreateClass be a static function of Parent? – mfnx Feb 07 '21 at 13:36
  • Just out of curiosity... Why would you want to prevent instantiation via new Child()? – mfnx Feb 07 '21 at 15:13

1 Answers1

2

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
  }
};
prog-fh
  • 13,492
  • 1
  • 15
  • 30
  • According to the question, create_instance() should return a pointer to Child, not Parent. – mfnx Feb 07 '21 at 13:39
  • @mfnx That is the exact purpose of my last sentence. – prog-fh Feb 07 '21 at 15:28
  • But then your factory instance could instantiate any type, doesn't it? I mean, not only a type which derives from Parent. – mfnx Feb 07 '21 at 15:37
  • 1
    @mfnx No, because of the tag which is only accessible to derived types (protected). But you could *double-check* with [is_base_of](https://en.cppreference.com/w/cpp/types/is_base_of). – prog-fh Feb 07 '21 at 15:52