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?
3 Réponses :
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);
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é.
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.
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:
Vous devez rendre le destructeur de la classe de base virtuel pour éviter certains comportements indéfinis.
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.
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); } }
Vous voudrez probablement changer
addChildNode(Node node)
enaddChildNode(const Node& node)
afin d'éviter le découpage deCube
enNode
.Notez que le simple ajustement de la signature de
addChildNode
neaddChildNode
pas tous les problèmes de découpage que cette conception semble susceptible de provoquer. L'existence destd::vector<Node> childNodes
, par exemple. Cela vaut peut-être la peine de reconsidérer la relation exacte entre lesCube
et lesNode
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".