Domanda Ereditarietà di C ++ in file separati che utilizzano # include e protezioni per inclusione


Sono nuovo di Stack Overflow e sto insegnando a me stesso C ++, ma sono ancora abbastanza un principiante. Dopo aver completato una bella parte del libro che sto usando (che potrebbe essere considerato obsoleto e / o non un grande libro) ho deciso di rafforzare alcuni concetti provandoli da solo, facendo riferimento al libro solo se necessario, ma io sembra essere bloccato I concetti che sto cercando di affrontare sono l'ereditarietà, il polimorfismo, i tipi di dati astratti (ADT) e la separazione del codice per le mie classi in file di intestazione (.h) e file C ++ (.cpp). Scusa in anticipo per il muro di testo, voglio solo essere chiaro e specifico dove devo essere.

Quindi, il mio obiettivo è creare classi di forme semplici che ereditino l'una dall'altra, laddove applicabile. Ho quattro classi: myPoly, myRectangle, myTriangle e mySquare. myPoly, se ho capito correttamente questo concetto, dovrebbe essere un ADT poiché uno dei metodi è una pura funzione virtuale (metodo area), poiché la creazione di un oggetto myPoly non è qualcosa che vorrei fare un utente delle mie classi. myRectangle e myTriangle derivano entrambi da myPoly ea sua volta mySquare deriva da myRectangle. Ho anche incluso il mio programma di test in cui ho pianificato di testare le mie lezioni. Sto usando Code :: Blocks 10.05 e continuo a ricevere il seguente errore quando costruisco il mio programma test.cpp:

undefined reference to 'myPoly::myPoly()'

Ottengo 42 errori simili tutti per i metodi della classe myPoly. Questo succede quando provo a creare i file .cpp per myRectangle e myTriangle. Con la ricerca che ho cercato di fare sui problemi che ho incontrato in questo piccolo progetto, mi sembra che ci sia qualcosa che non va nelle mie protezioni di inclusione o nelle mie dichiarazioni #include, e qualcosa non viene incluso correttamente o viene incluso troppe volte. Inizialmente stavo fornendo il file .cpp per myPoly a myRectangle e myTriangle, ma ho letto in un paio di punti che il file .h di myPoly è più efficiente e in qualche modo include automaticamente il suo .cpp. Se qualcuno può fornire alcune informazioni su questo, sarebbe molto apprezzato. Ricordo anche qualcosa su come usare le virgolette nelle tue istruzioni di inclusione è diverso dall'uso delle parentesi angolari. Di seguito sono riportati tutti e nove i file che ho realizzato per il mio piccolo progetto. La maggior parte dei commenti sono piccole note o promemoria per me.

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;
}

Sono molto curioso del motivo per cui sto ricevendo questi errori o perché qualcosa che ho fatto potrebbe non essere considerato buono / migliore pratica, al contrario di ricevere solo una riga di codice per far sparire i miei errori.


15
2017-07-31 19:54


origine


risposte:


Le tue guardie di inclusione sembrano a posto. Se non lo fossero, molto probabilmente otterresti un errore del compilatore, incluse le informazioni sul numero di file e di linea. L'errore che hai postato sembra più un errore di linker.

Tuttavia, c'è un "problema" con il tuo codice. Come regola generale, dovresti solo #include .h file e non file .cpp.

Ora per arrivare alla soluzione: non ho familiarità con Code :: Blocks me stesso. Tuttavia, spero di poter fornire alcune informazioni generali che ti indirizzino nella giusta direzione. Alcuni compilatori che ho usato in passato di default mi hanno permesso di compilare un singolo file C ++ ed eseguire il programma. Per compilare un programma con più di un file, ho dovuto creare un progetto. (La maggior parte dei compilatori moderni ti obbliga a creare un progetto dall'inizio.) Con questo in mente, ti suggerisco di verificare come creare un progetto per il tuo programma in Code :: Blocks.


5
2017-07-31 20:09



Da un punto di vista del codice (almeno quello che ho guardato attraverso), sembra abbastanza buono, ma:

Ci sono due cose da considerare:

  1. Non includere direttamente i file cpp. Ad esempio, in mySquare.h, #include "myRectangle.cpp" dovrebbe essere #include "myRectangle.h". Vuoi includere l'interfaccia / le dichiarazioni fornite nel file di intestazione che indicano al programma come creare la classe, non solo le definizioni di funzione.

  2. In secondo luogo, assicurati di compilare tutti i tuoi file oggetto. Non conosco i blocchi di codice, ma se tu stessi usando g ++ o qualcosa del genere, vorresti farlo g++ main.cpp myPoly.cpp mySquare.cpp etc. per tutti i file. Un errore come questo può accadere se si dimentica myPoly.cpp, ad esempio, perché non dovrebbero essere incluse definizioni per le sue funzioni.


4
2017-07-31 20:05



Tutto sembra a posto, in realtà. Probabilmente è semplice come non includere myPoly.obj quando colleghi il tuo programma. Non ho familiarità con Code :: Blocks (anche se so che è abbastanza popolare) ma presumo che se, per esempio, fai clic su test.cpp e scegli "Esegui", Code :: Blocks proverà a creare un programma da solo quel file sorgente. Dovrai includere tutti i file sorgente rilevanti in ogni programma che crei.


3
2017-07-31 19:59



In aggiunta a ciò che hanno detto gli altri ragazzi: non stai facendo eredità ...

Quando lo fai

class Poly
{
   Poly();
   ~Poly();
}

class Rect : public Poly()
{
   Rect();
   ~Rect();
}

Devi dichiarare il costruttore del bambino nel seguente modo:

Rect::Rect() : Poly()
{

}

Il bambino deve essere costruito solo dopo che il padre ha terminato la costruzione.


-2
2017-07-31 20:35