web-dev-qa-db-fra.com

Héritage C ++ dans des fichiers séparés à l'aide de #include et d'inclusion Guards

Je suis nouveau sur Stack Overflow et j'apprends moi-même le C++, mais je suis encore un débutant. Après avoir terminé une bonne partie du livre que j'utilise (qui peut être considéré comme obsolète et/ou pas un excellent livre), j'ai décidé de renforcer certains concepts en les essayant moi-même, en référençant le livre uniquement si nécessaire, mais je semblent être coincés. Les concepts que j'essaie d'aborder sont l'héritage, le polymorphisme, les types de données abstraits (ADT) et la séparation du code de mes classes en fichiers d'en-tête (.h) et fichier C++ (.cpp). Désolé d'avance pour le mur de texte, je veux juste être clair et précis là où je dois être.

Mon objectif est donc de créer des classes de formes simples qui héritent les unes des autres le cas échéant. J'ai quatre classes: myPoly, myRectangle, myTriangle et mySquare. myPoly, si j'ai bien compris ce concept, devrait être un ADT car l'une des méthodes est une fonction virtuelle pure (méthode area), car la création d'un objet myPoly n'est pas quelque chose que je voudrais qu'un utilisateur de mes classes fasse. myRectangle et myTriangle dérivent tous les deux de myPoly et à leur tour mySquare dérive de myRectangle. J'ai également inclus mon programme de test où j'avais prévu de tester mes cours. J'utilise Code :: Blocks 10.05 et j'obtiens toujours l'erreur suivante lorsque je crée mon programme test.cpp:

undefined reference to 'myPoly::myPoly()'

J'obtiens 42 erreurs similaires toutes pour les méthodes de la classe myPoly. Cela se produit lorsque j'essaie de créer les fichiers .cpp pour myRectangle et myTriangle également. Avec les recherches que j'ai essayées de faire sur les problèmes que j'ai rencontrés avec ce petit projet, j'ai l'impression que quelque chose ne va pas avec mes gardes d'inclusion ou mes déclarations #include, et quelque chose n'est pas inclus correctement ou est inclus trop souvent. Au début, je fournissais le fichier .cpp pour myPoly à myRectangle et myTriangle, mais j'ai lu à quelques endroits que l'inclusion du fichier .h pour myPoly était plus efficace et incluait automatiquement son .cpp. Si quelqu'un peut donner un aperçu de cela, ce serait grandement apprécié. Je me souviens également de la différence entre l'utilisation de guillemets dans vos instructions d'inclusion et l'utilisation des crochets angulaires. Voici les neuf fichiers que j'ai créés pour mon petit projet. La plupart des commentaires sont de petites notes ou des rappels pour moi.

myPoly.h

//Practice with inheritance, polymorphism, and Abstract Data Types
//header file for Polygon class

#ifndef MYPOLY_H
#define MYPOLY_H

class myPoly
{
    public:
        //constructor
        //const reference pass because the values w and h don't change and reference avoid the time it takes to copy large
        //  objects by value (if there were any)
        myPoly();
        myPoly(const float & w, const float & h);

        //destructor
        virtual ~myPoly();

        //accessors
        float getWidth();
        float getHeight();
        void setWidth(const float & w);
        void setHeight(const float & h);

        virtual float area() = 0;

    private:
        float width, height;
};

#endif

myPoly.cpp

//Practice with inheritance, polymorphism, and Abstract Data Types
//implementation file for myPoly class

#include "myPoly.h"

//constructor
myPoly::myPoly()
{
    setWidth(10);
    setHeight(10);
}

myPoly::myPoly(const float & w, const float & h)
{
    setWidth(w);
    setHeight(h);
}

//destructor
myPoly::~myPoly() {}

//accessors
float myPoly::getWidth() {return width;}
float myPoly::getHeight() {return height;}

void myPoly::setHeight(const float & w) {width = w;}
void myPoly::setWidth(const float & h) {height = h;}

//pure virtual functions have no implementation
//area() is handled in the header file

myRectangle.h

//Practice with inheritance, polymorphism, and Abstract Data Types
//declaration file for myRectangle class

#ifndef MYRECTANGLE_H
#define MYRECTANGLE_H

#include "myPoly.h"

class myRectangle : public myPoly
{
    public:
        //constructor
        myRectangle();
        myRectangle(const float & w, const float & h);

        //destructor
        ~myRectangle();

        //this doesn't need to be virtual since the derived class doesn't override this method
        float area();
};

#endif

myRectangle.cpp

//Practice with inheritance, polymorphism, and Abstract Data Types
//implementaion file for the myRectangle class

//get a vauge compiler/linker error if you have virtual methods that aren't implemented (even if it ends up being just
//  a 'stub' method, aka empty, like the destructor)

#include "myRectangle.h"

myRectangle::myRectangle()
{
    setWidth(10);
    setHeight(10);
}

myRectangle::myRectangle(const float & w, const float & h)
{
    setWidth(w);
    setHeight(h);
}

myRectangle::~myRectangle()
{
}

float myRectangle::area()
{
    return getWidth() * getHeight();
}

myTriangle.h

//Practice with inheritance, polymorphism, and Abstract Data Types
//declaration file for myTriangle class

#ifndef MYTRIANGLE_H
#define MYTRIANGLE_H

#include "myPoly.h"

//imagine the triangle is a right triangle with a width and a height
//  |\
//  | \
//  |  \
//  |___\

class myTriangle : public myPoly
{
    public:
        //constructors
        myTriangle();
        myTriangle(const float & w, const float & h);

        //destructor
        ~myTriangle();

        //since nothing derives from this class it doesn't need to be virtual and in turn neither does the destructor
        float area();
};

#endif

myTriangle.cpp

//Practice with inheritance, polymorphism, and Abstract Data Types
//implementation file for myTriangle class

#include "myTriangle.h"

myTriangle::myTriangle()
{
    setWidth(10);
    setHeight(10);
}

myTriangle::myTriangle(const float & w, const float & h)
{
    setWidth(w);
    setHeight(h);
}

myTriangle::~myTriangle()
{
}

float myTriangle::area()
{
    return getWidth() * getHeight() / 2;
}

mySquare.h

//Practice with inheritance, polymorphism, and Abstract Data Types
//declaration file for mySquare class

#ifndef MYSQUARE_H
#define MYSQUARE_H

#include "myRectangle.cpp"

class mySquare : public myRectangle
{
    public:
        //constructors
        mySquare();
        //explicity call the myRectangle constructor within this implementation to pass w as width and height
        mySquare(const float w);

        //destructor
        ~mySquare();
};

#endif

mySquare.cpp

//Practice with inheritance, polymorphism, and Abstract Data Types
//implementation file for mySquare class

#include "mySquare.h"

mySquare::mySquare()
{
    setWidth(10);
    setHeight(10);
}

mySquare::mySquare(const float w)
{
    myRectangle::myRectangle(w, w);
}

mySquare::~mySquare()
{
}

test.cpp

//Practice with inheritance, polymorphism, and Abstract Data Types
//main class that uses my shape classes and experiments with inheritance, polymorphism, and ADTs

#include "myRectangle.cpp"
//#include "mySquare.cpp"
#include "myTriangle.cpp"

#include <iostream>

int main()
{
    myPoly * shape = new myRectangle(20,20);

    return 0;
}

Je suis très curieux de savoir pourquoi j'obtiens ces erreurs ou pourquoi quelque chose que j'ai fait peut ne pas être considéré comme une bonne/meilleure pratique, au lieu de simplement recevoir une ligne de code pour faire disparaître mes erreurs.

22
RIBdot

Vos gardes d'inclusion ont l'air bien. Si ce n'était pas le cas, vous obtiendriez très probablement une erreur de compilation, y compris des informations sur le fichier et le numéro de ligne. L'erreur que vous avez publiée ressemble plus à une erreur de l'éditeur de liens.

Cependant, il y a un "problème" avec votre code. En règle générale, vous ne devez que #include fichiers .h et non fichiers .cpp.

Passons maintenant à la solution: je ne connais pas moi-même Code :: Blocks. Cependant, j'espère pouvoir vous donner des informations générales qui vous orienteront dans la bonne direction. Certains compilateurs que j'ai utilisés dans le passé par défaut m'ont permis de compiler un seul fichier C++ et d'exécuter le programme. Pour compiler un programme avec plus d'un fichier, j'ai dû créer un projet. (La plupart des compilateurs modernes vous obligent à créer un projet depuis le début.) Dans cet esprit, je vous suggère de vérifier comment créer un projet pour votre programme dans Code :: Blocks.

8
Code-Apprentice

Du point de vue du code (du moins ce que j'ai examiné), cela semble assez bon, mais:

Il y a deux choses à considérer:

  1. N'incluez pas directement les fichiers cpp. Par exemple, dans mySquare.h, #include "myRectangle.cpp" devrait être #include "myRectangle.h". Vous souhaitez inclure l'interface/les déclarations fournies dans le fichier d'en-tête qui indiquent au programme comment créer la classe, pas seulement les définitions de fonction.

  2. Deuxièmement, assurez-vous que vous compilez avec tous vos fichiers objets. Je ne connais pas les blocs de code, mais si vous utilisiez g ++ ou quelque chose comme ça, vous voudriez faire g++ main.cpp myPoly.cpp mySquare.cpp etc. pour tous les fichiers. Une erreur comme celle-ci peut se produire si vous oubliez myPoly.cpp, par exemple, car aucune définition de ses fonctions ne sera incluse.

4

Tout semble bien, en fait. C'est probablement aussi simple que de ne pas inclure myPoly.obj lorsque vous liez votre programme. Je ne connais pas Code :: Blocks (bien que je sache qu'il est assez populaire) mais je suppose que si vous venez, par exemple, cliquez sur test.cpp et choisissez "Exécuter", que Code :: Blocks essaiera de construire un programme à partir de juste ce fichier source. Vous devrez inclure tous les fichiers source pertinents dans chaque programme que vous créez.

3