0
votes

Appel d'une fonction parent à partir d'une autre fonction à l'aide du C ++ enfant

J'ai donc une classe parent qui est:

#include "cube.h"

void addCubes(){
     Cube mainCube;
     for(int i = 0; i < 10; i++){
         Cube c;
         mainCube.addChildNode(c);
      }
}

Et un enfant héritant de cette classe:

class Cube : public Node
{
public:
    Cube();
};

Maintenant, j'ai un autre fichier qui a une fonction qui utilise la classe enfant:

class Node
{
public:
    Node();

    void setParentNode(Node* parent) {this->parentNode = parent;}
    Node* getParentNode() {return this->parentNode;}

    std::vector<Node> getChildNodes(){return this->childNodes;}

    void addChildNode(Node* node);
    void removeNode();

private:
    std::vector<Node*> childNodes;
    Node* parentNode = nullptr;
};

Le problème est que mainCube ne voit pas la fonction addChildNode que possède le parent. Quel est l'intérêt d'hériter d'une autre classe si les fonctions parents ne sont pas accessibles depuis un autre endroit en utilisant la classe enfant?


4 commentaires

Vous voudrez probablement changer addChildNode(Node node) en addChildNode(const Node& node) afin d'éviter le découpage de Cube en Node .


Notez que le simple ajustement de la signature de addChildNode ne addChildNode pas tous les problèmes de découpage que cette conception semble susceptible de provoquer. L'existence de std::vector<Node> childNodes , par exemple. Cela vaut peut-être la peine de reconsidérer la relation exacte entre les Cube et les Node et pourquoi vous avez choisi l'héritage.


@NathanPierson a un point valide. Jetez un œil à des threads comme celui-ci: stackoverflow.com/questions/56464702/avoiding-object-slicing


Comme d'autres l'ont dit, vous pouvez appeler une méthode d'une classe parent à partir de l'enfant exactement comme vous appelleriez l'une des propres méthodes de l'enfant - tant que la méthode parente a la visibilité de "public" ou "protected".


3 Réponses :


1
votes

Non, les fonctions publiques des classes parentes peuvent être appelées à partir de l'objet enfant. Cependant, le prototype de la fonction est

Node * pn = &b;

Il prend donc un objet Node et non un objet Cube . Ainsi, votre compilateur ne peut pas trouver une fonction qui prend un objet Cube et donc l'erreur.

Le correctif consiste à utiliser un pointeur vers Node ou une référence à Node lors de la déclaration / définition de la fonction.

Donc, votre fonction devrait être

Cube b;   
Node &a = b;

Dans ce cas, l'objet Node peut être passé à la fonction et le compilateur le trouvera.

Encore mieux serait d'avoir

// if you aren't looking to modify the passed object inside addChildNode
void addChildNode(const Node & node); 

Ce qui suit est très bien et donc la fonction fonctionnera

void addChildNode(Node & node);

ou

void Node::addChildNode(Node node);


9 commentaires

Doit être const Node & node si vous souhaitez répliquer l'intention de l'auteur (qui n'inclut pas la capacité de la fonction à modifier l'objet d'entrée).


@goodvibration - oui, c'est vrai. Je donne juste le prototype de fonction le plus simple possible qui compilera


C'est plus simple, mais c'est généralement faux, car changer la fonction addChildNode(Node node) en addChildNode(Node& node) ouvre la possibilité de tomber dans un monde de mal. Mieux vaut donner à cet utilisateur une solution correcte complexe plutôt qu'une simple solution incorrecte.


Merci, mais mon problème est qu'il dit que addChildNode n'existe pas quand je fais mainCube.addChildNode;


@ Pierre-OlivierLeroux - c'est parce qu'il ne trouve pas addChildNode qui prend Cube. addChildNode qui prend un Node & addChildNode qui prend un Cube sont 2 fonctions différentes


Puisque Cube hérite publiquement de Node , cela ne devrait pas être le problème. La non-concordance entre Node* et Cube , cependant, entraînerait l'échec de la compilation du code.


@NathanPierson - sa question originale avait la méthode prenant un objet Node. Après avoir répondu, il l'a changé pour prendre un Node *.


@ user93353 Exactement. Mais la version qui prend un Node par valeur doit toujours prendre un argument de type Cube car le Cube peut être découpé en un Node . Il présentera probablement un comportement indésirable, mais il se compilera.


@ user93353, NathanPierson a raison. Même avec le passage par valeur, il est possible de passer un objet enfant. L'objet est tranché.



0
votes

En supposant que vous ayez partagé l'intégralité de l'implémentation de votre code. Vous n'avez pas défini le corps de la fonction addChildNode , similaire à setParentNode etc. Vous devez faire quelque chose comme childNodes.push_back(node); à l'intérieur de cette fonction.

Remarque: Il est également nécessaire de transmettre l'entrée à addChildNode comme indiqué dans la réponse de @ user93353. Définissez également childNodes comme std::vector<Node *> afin d'éviter le découpage des objets.


0 commentaires

1
votes

Les classes dérivées devraient pouvoir voir la fonction addChildNode si vous conservez la signature de la fonction alignée. Ce n'est pas un gros problème. Cependant, il existe quelques problèmes plus «graves» avec votre code:

  1. Vous devez rendre le destructeur de la classe de base virtuel pour éviter certains comportements indéfinis.

  2. Vous devez concevoir soigneusement la propriété des nœuds. Je suppose que vous voulez que la classe Node possède et gère ses nœuds enfants. Cela signifie que la fonction addChildNode prend en fait la propriété de l'objet nœud transmis, et il doit également être supprimé lors de la destruction.

  3. Dans la fonction addCubes() , il y a une boucle qui continue d'appeler la fonction addChildNode mais passe la variable locale Cube c; qui sera hors de portée et détruit après la boucle. Ainsi, l'objet parent mainCube contiendra des pointeurs vers des objets déjà détruits, et cela provoquera un crash.

Après avoir résolu tous ces problèmes, votre code ressemble à ceci:

#include <memory>

class Node
{
public:
    Node() {};
    virtual ~Node() {};

    void setParentNode(Node* parent) {this->parentNode = parent;}
    Node* getParentNode() {return this->parentNode;}

    std::vector<std::shared_ptr<Node>>& getChildNodes(){return this->childNodes;}

    void addChildNode(std::shared_ptr<Node> node) {
        childNodes.push_back(std::move(node));
    };
    void removeNode();

private:
    // childNodes own elements in it, they will be deleted automatically.
    std::vector<std::shared_ptr<Node>> childNodes;
    Node* parentNode = nullptr;
};
class Cube: public Node
{
public:
    Cube() {};
};
void addCubes(){
    Cube mainCube;
    for(int i = 0; i < 10; i++){
        auto c = std::make_unique<Cube>();
        mainCube.addChildNode(std::move(c));
    }
}

Il est préférable d'utiliser des pointeurs intelligents pour gérer la mémoire, et le code est plus élégant et plus facile à lire, et il est plus difficile de faire des erreurs :-).

class Node
{
public:
    Node() {};
    virtual ~Node() {
        for(auto n: childNodes) delete n;
    };

    void setParentNode(Node* parent) {this->parentNode = parent;}
    Node* getParentNode() {return this->parentNode;}

    std::vector<Node*> getChildNodes(){return this->childNodes;}

    void addChildNode(Node* node) {
        childNodes.push_back(node);
    };
    void removeNode();

private:
    std::vector<Node*> childNodes;
    Node* parentNode = nullptr;
};

class Cube : public Node
{
public:
    Cube() {};
};

void addCubes(){
    Cube mainCube;
    for(int i = 0; i < 10; i++){
        Cube *c = new Cube();
        mainCube.addChildNode(c);
    }
}


0 commentaires