0

I want to have a parent class which it stores all child class and the child class can have a derived classes which each classes is unique. that's why I want to initialize class in a function but a class is a parameter. I don't know what to describe but let's look at the code. I'm beginner btw.

class Child {
    string child_name;
}
class Jame : public Child {
    void sit() {
        cout << "Jame can sit" << endl; 
    }
}
class Harry : public Child {
    void sleep() {
        cout << "Harry can stand" << endl;
    }
}

class Parent {
    vector <Child> children;
    void addChild(string class_name) { //Prototype
        [classname] child; //EX. Jame child;
        child.child_name = class_name;
        
        children.push_back(child);
    }
    Child* getChild(string child_name) {
        for(int i = 0; i < children.size(); i++)
            if(children.at(i).child_name == child_name)
                return &children.at(i);
    }
};

int main() {

    Parent parent;
    parent.addChild("Harry");
    Child* child = parent.getChildren("Harry");
    child->sleep(); //Harry can stand.
    child->sit(); //error: undefined function.
    
    return 0;   
}

Something like this. If you have any other ways help me please, I'm very new and I don't know what it's called.

  • 1
    `class Harry : public Harry` doesn't look right. – Igor Tandetnik Aug 22 '21 at 23:28
  • You'll want Child to have a `virtual void sit() { }` and `virtual void sleep() { }`. Harry should derive from Child, not from itself. The vector will have to be of pointers, or better yet smart pointers, because polymorphism requires pointers or references... otherwise you'll get slicing. – Eljay Aug 22 '21 at 23:31
  • @IgorTandetnik yep that's my mistake. – KornYellow Aug 22 '21 at 23:36
  • @Eljay thanks for the vector suggestion, but I think you don't quite picking up what I'm putting down, the thing is I want a Child class to be a base class which Parent can store it in, but I want to make a class which derive from it so I can make custom function and only use it in that class. Hope that's making sense. – KornYellow Aug 22 '21 at 23:41

2 Answers2

0

It seems to me that you are misinterpreting something, or at least the way you implement this is very weird. It seems as if you want to have Harry and Jame as instances of Child and not actually a derived class of Child. The reason why I say this is, because Harry and Jame do not seem to be designed as classes, but as interfaces to controlling one instance of a single class. A better way of handling this is that when you define Child you could do it like:

class Child{
    private:
        bool canSleep;
        bool canSit;
        std::string name;
    public:
        Child(std::string _name,bool sleep, bool sit)
        : name(_name), canSleep(sleep), canSit(sit) {}
        void sit(){
            if(canSit) std::cout << name << " can sit\n";
        }
        void sleep(){
            if(canSleep) std::cout << name << " can sleep\n";
        }
};

You could also extend this with enums implementing flags instead of booleans, but that just helps with usability.

As for the actual question and one solution (and some issues with the code):

There is no syntax (as far as I'm aware) that would allow you to do this, the way presented here, but you can use templates. If you can use C++20 you can even constrain it easily so bad calls are caught compile time, and also produce way less noise. You can make AddChild into a template like:

// Only for c++20
#include <concepts>

class Parent {
    private:
        std::vector<Child*> children;
    public:
        template<typename Child_type>
        requires std::derived_from<Child_type,Child> // Only for C++20
        void addChild(std::string name){
             children.push_back(new Child_type(name));
        }
};

Now this is a very basic solution and can be called by parent.addChild<Harry>("Harry"); for example.

There are however other issues that I somewhat tried to mitigate. Firstly in a class every member you declare is private by default, meaning that no one outside the class (apart from friend functions, which would be a really hacky solution in this case), can access those members (including functions).

You also can't really store Harry, where a Child should go. You can store a pointer to Harry where a pointer to a Child should go however. I used raw pointers (which you should generally avoid), but you should look up std::unique_ptr and std::shared_ptr, as raw pointers are a pain.

You would also need to define sit and sleep as virtual functions to be able to reference them from a pointer to a Child (without casting to the derived types, which can be problematic if not done properly (edit: see other answer for doing it properly with dynamic_cast)).

Finally it seems you are using using namespace std;, so obligatory: It's bad practice and can lead to very difficult to debug issues.

Lala5th
  • 1,137
  • 7
  • 18
0

As I understand, there is really two questions here:

  1. How to get addChild("Harry") to find the right child class and register it.
  2. How to have certain functions which only work for particular child classes.

Number one is a fairly standard factory/registrar class, which might be good to read up on (here, but you can find other resources online). Maybe you could do something like this:

class Parent {
    // This needs to be unique_ptr<Child>!
    std::map<std::string, std::unqiue_ptr<Child>> children;

    template<typename ChildType>
    void addChild(string class_name) {
        // Make sure ChildType is derived from Child
        static_assert(std::is_base_of<Child, ChildType>::value);
        children[class_name](std::make_unique<ChildType>());
    }
    ...
}

int main() {
    Parent parent;

    // You'll need to give it the actual child type here
    parent.addChild<Harry>("Harry");

    ...
}

As for number two... there isn't really a way to get that to work with polymorphism like you'd want. Depending on how you're using this class, dynamic_cast might be worth trying:

    Child* child = parent.getChildren("Harry");
    if (Harry* harry = dynamic_cast<Harry*>(child)) {
        harry->sleep();
    } else if (Jame* jame = dynamic_cast<Jame*>(child)) {
        jame->sit();
    }
mattlangford
  • 1,260
  • 1
  • 8
  • 12
  • The real thing is I'm making a Game with an Instance class inside of an Object class. An object class contain a vector of instances which instance is a base class of other class like Player, Enemy, Bullet, etc. so different class can have it own function but contain in the same object class and each instances can manage other instances with a called of a function inside an instance class. I'm a beginner and I think I starting it off a little too ambitious. Thank you by the way!! I'll give it a try. – KornYellow Aug 23 '21 at 05:42
  • https://en.m.wikipedia.org/wiki/Entity_component_system Is another way you could think about approaching the problem which (once you wrap your head around it) can make the whole process a lot cleaner in my opinion. – mattlangford Aug 23 '21 at 13:04
  • And one more: https://en.m.wikipedia.org/wiki/Object_pool_pattern would be to have multiple instance classes for each object type – mattlangford Aug 23 '21 at 13:13