C++ Inheritance with parent “knowing” the children

僤鯓⒐⒋嵵緔 提交于 2019-12-24 13:09:44

问题


Hello I have a global array of let's say

array = ["A", "B", "C", "D", "E"]

I want to design a parent class with 5 children classes like this

Parent, Child1, Child2, Child3, Child4, Child5

The Parent class will have a method name getID();

How can i design such that this behavior occur

Child1.getID() is array[0] gives "A"
Child2.getID() is array[1] gives "B"
Child3.getID() is array[2] gives "C"
Child4.getID() is array[3] gives "D"
Child5.getID() is array[4] gives "E"

Is this possible?

The reason is that I don't want to keep copy and pasting getID() for 5 children. It would be nice to just write getID() in parent class.


回答1:


I'm going to assume that the aim is to avoid repetition of the body of getID() in each child class. But I also think moving the array into the parent class is "not ideal".

There are many options you can use to avoid the boilerplate code. Which one works best for you depends heavily on the real use case for the classes.

Use only one child class and a factory method in parent

Push the common code into a base class and add a factory method to the parent class (You could even merge the parent class and base class if appropriate).

struct ChildBase : public Base {
  explicit ChildBase(const char* name) : name(name) {};
  const char * name; 
  const char * getID() { return name; }
}

struct Base {
   ChildBase getChild1() { return ChildBase("A"); }
   ChildBase getChild2() { return ChildBase("B"); }
}

or even

struct Base {
   const char* ids = { "A", "B", "C", ...};
   ChildBase getChild(int i) { return ChildBase(ids[i]); }
}

This second form easily generalizes to loading the configuration at run time.

Use a base class

Push the common code into a base class.

Usually this would look like:

struct ChildBase : public Base {
  ChildBase(const char* name) : name(name) {};
  const char * name; 
  const char * getID() { return name; }
}

struct Child1 : public ChildBase {
    Child1() : ChildBase("A") {};
}

struct Child2 : public ChildBase {
    Child2() : ChildBase("B") {};
}

but this doesn't save you much since you still need to customize the constructor. You can reduce the cruft using a templated base class instead

template<const char* ID>
struct ChildBase() { 
   const char* getID() { return ID; }
};

struct Child1 : public ChildBase<"A">();
struct Child2 : public ChildBase<"B">();

Macros

Yes, macros are evil. But they can solve a problem like this pretty easily

#define DefineChild(CLASS, ID) \
  struct CLASS : public Base { \
    const char * getID() { \
      return ID; \
    } \
  }

DefineChild(Child1, "A");
DefineChild(Child2, "B");

Code Generation

Generate a base class for each of the child classes in your build script. (Or even each of the child classes directly if they're as empty as you say.)

Definition File - this is how you configure what gets generated. You'll need to write a generation script, but that can usually be pretty trivial.

Child1 "A"
Child2 "B"
Child3 "C"
...

Generated code - The output of your generation script

// Generated code - do not edit
struct Child1Base : public Base {
    const char* getID() { return "A"; }
}
...

Application Code - Here you can customize the behaviour of the generated code if needed.

struct Child1 : public Child1Base {}
struct Child2 : public Child2Base {}
struct Child3 : public Child3Base {}
struct Child4 : public Child4Base {}



回答2:


No, not like that. A virtual method in a subclass that overrides virtual methods from its multiple superclasses has no indication of which superclass's virtual method was invoked.

An alternative design is to have each superclass's getid() method be non-virtual, and be a wrapper that invokes an abstract getMyId(), passing it a single parameter, that's unique to each superclass.

The parent subclass implements the abstract getMyId() method, and uses the parameter that the superclass passed, that indicates which superclass invoked the abstract method.

Very simple, and straightforward.




回答3:


One way to do this, assuming that I understand the question correctly, is to look up the string based on the type id of the class, e.g.,

#include <typeinfo>         // Necessary for using `typeid`.
#include <typeindex>        // std::type_index
#include <map>              // Consider using `std::unordered_map` instead.

struct Parent
{
    auto id() const -> char const*;
    virtual ~Parent() {}    // Need something virtual for `typeid`.
};

struct Child1: Parent {};
struct Child2: Parent {};
struct Child3: Parent {};
struct Child4: Parent {};
struct Child5: Parent {};

auto Parent::id() const
    -> char const*
{
    using namespace std;
    static const map<type_index, char const*> ids =
    {
        {typeid( Child1 ), "A"},
        {typeid( Child2 ), "B"},
        {typeid( Child3 ), "C"},
        {typeid( Child4 ), "D"},
        {typeid( Child5 ), "E"}
    };

    return ids.at( typeid(*this) );
}

#include <iostream>
using namespace std;
auto main()
    -> int
{ cout << Child1().id() << Child3().id() << "\n"; }


来源:https://stackoverflow.com/questions/37872126/c-inheritance-with-parent-knowing-the-children

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!