Calling constructor of potential child from base i

2019-08-07 22:44发布

问题:

How can someone implement this pattern:

  class Base {//doesn't know anything about potential descendant-classes, like Child
  public:
      Base * foo( void) {
        //some code
          return ( Base *) new !Child-constructor!();
      }
  };

  class Child : public Base { };

//—————————————————————————————————————————————————
  #include <iostream>
  #include <typeinfo>

  using namespace std;

  int main( void) {
      Base * base_p = Child().f();

      cout << typeid( *base_p).name(); //expected to get "Child"

  return 0;
  }

I can't find the right syntax for this type of constructions (calling the "potential" child constructor).

UPD: I forgot to mention (didn't think that there can be missunderstanding), that Child class must not be known in definition of Base. So I wanted foo to call the constructor of the class in which it's gonna be inherited.

回答1:

"... that Child class must not be known in definition of Base. So I wanted foo to call the constructor of the class in which it's gonna be inherited."

IMHO the easiest way is to provide a templated factory function with Base

class Base {
public:
    template<class Derived>
    static std::unique_ptr<Base> foo( void) {
      //some code
      return std::unique_ptr<Base>(new Derived());
    }
};

class Child : public Base {
public:
    Child() {}
    virtual ~Child() {}
};

int main() {
    std::unique_ptr<Base> p = Base::foo<Child>();
    return 0;
}

Check the compilable sample here please.



回答2:

This design is awful, btw.

 //terriblecode.hpp

 struct Base
 {
    Base * foo(void);
 };

 struct Child : public Base{};

 //terriblecode.cpp
 Base* Base::foo() {return (Base*) new Child();}

The definition of child isn't needed untill the definition of foo. Separate your declaration from your definition of member functions, and it is pretty easy to do.



回答3:

Just need to make sure you let the compiler know about your derived class before the first use:

class Child; // forward declaration

class Base {
public:
  Base * foo( void);
};

class Child : public Base { };

// the function needs to be defined after the "Child" is known
Base * Base::foo( void) {
  //some code
    return new Child; // will be automatically type-cast to base class
}

//—————————————————————————————————————————————————
#include <iostream>
#include <typeinfo>

using namespace std;

int main( void) {
  Base * base_p = Child().f();

  cout << typeid( *base_p).name(); //expected to get "Child"

  return 0;
}

However, I'd recommend a different pattern:

class Child; // forward declaration

class Base {
public:
  static Base * foo( void); // static "factory" method
};

class Child : public Base { };

// the function needs to be defined after the "Child" is known
Base * Base::foo( void) {
  //some code
    return new Child; // will be automatically type-cast to base class
}

//—————————————————————————————————————————————————
#include <iostream>
#include <typeinfo>

using namespace std;

int main( void) {
  Base * base_p = Base::f(); // use scope resolution, not object's method

  cout << typeid( *base_p).name(); //expected to get "Child"

  return 0;
}