Подтвердить что ты не робот

Наследование С++ в отдельных файлах с использованием #include и Inclusion Guard

Я новичок в Qaru и преподаю сам С++, но я все еще довольно новичок. После того, как я воспользовался (который может считаться датированным и/или не большой книгой), я решил изменить некоторые концепции, попробовав их самостоятельно, ссылаясь на книгу только при необходимости, но я похоже, застряли. Понятия, которые я пытаюсь решить, это наследование, полиморфизм, абстрактные типы данных (ADT) и разделение кода для моих классов на файлы заголовков (.h) и файл С++ (.cpp). Извините заранее за стену текста, я просто хочу быть ясным и конкретным, где мне нужно быть.

Итак, моя цель - создать простые классы формы, которые наследуют друг от друга, где это применимо. У меня есть четыре класса: myPoly, myRectangle, myTriangle и mySquare. myPoly, если я правильно понял это понятие, должен быть ADT, поскольку один из методов - это чистая виртуальная функция (метод области), так как создание объекта myPoly - это не то, что я хочу, чтобы пользователь моих классов делал. myRectangle и myTriangle выводятся из myPoly, и в свою очередь mySquare происходит из myRectangle. Я также включил свою тестовую программу, где планировал тестирование своих классов. Я использую Code:: Blocks 10.05 и продолжаю получать следующую ошибку при создании моей программы test.cpp:

undefined reference to 'myPoly::myPoly()'

Я получаю 42 одинаковых ошибки для методов класса myPoly. Это происходит, когда я пытаюсь создать .cpp файлы для myRectangle и myTriangle. С исследованиями, которые я пытался делать с проблемами, с которыми сталкивался в этом маленьком проекте, я чувствую, что что-то не так с моими охранниками включения или с моими заявлениями #include, и что-то не входит в комплект поставки должным образом или слишком часто включается. Сначала я предоставлял файл .cpp для myPoly для myRectangle и myTriangle, но читал в нескольких местах, в том числе файл .h для myPoly, был более эффективным, а некоторые - как автоматически включали его .cpp. Если кто-то может дать некоторое представление об этом, мы будем весьма признательны. Я также помню кое-что о том, как использование кавычек в ваших операторах включения отличается от использования угловых скобок. Ниже приведены все девять файлов, которые я сделал для моего маленького проекта. Большинство комментариев - это небольшие заметки или напоминания для меня.

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

Мне очень любопытно, почему я получаю эти ошибки или почему то, что я сделал, не может считаться хорошей/лучшей практикой, а не просто получать строку кода, чтобы мои ошибки уходили.

4b9b3361

Ответ 1

Ваши охранники включения выглядят отлично. Если это не так, вы, скорее всего, получите ошибку компилятора, включая информацию о файле и строке. Вывешенная вами ошибка больше похожа на ошибку компоновщика.

Однако в вашем коде есть одна "проблема". Как правило, вы должны использовать только файлы #include.h, а не файлы .cpp.

Теперь, чтобы перейти к решению: я сам не знаком с Code:: Blocks. Однако, я надеюсь, что могу дать некоторую общую информацию, которая укажет вам в правильном направлении. Некоторые компиляторы, которые я использовал в прошлом по умолчанию, позволили мне скомпилировать один файл на С++ и запустить программу. Чтобы скомпилировать программу с несколькими файлами, мне пришлось создать проект. (Большинство современных компиляторов заставляют вас создавать проект с самого начала.) Имея это в виду, я предлагаю вам проверить, как создать проект для вашей программы в Code:: Blocks.

Ответ 2

С точки зрения кода (по крайней мере, что я просматривал), это выглядит довольно хорошо, но:

Можно рассмотреть две вещи:

  • Не включайте напрямую файлы cpp. Например, в mySquare.h, #include "myRectangle.cpp" должен быть #include "myRectangle.h". Вы хотите включать в себя интерфейс/декларации, представленные в файле заголовка, которые сообщают программе, как сделать класс, а не только определения функций.

  • Во-вторых, убедитесь, что вы компилируете все ваши объектные файлы. Я не знаю блоков кода, но если вы использовали g++ или что-то вроде этого, вы бы хотели сделать g++ main.cpp myPoly.cpp mySquare.cpp etc. для всех файлов. Подобная ошибка может произойти, если вы забудете myPoly.cpp, например, потому что не будут включены определения для его функций.

Ответ 3

На самом деле все выглядит хорошо. Это, вероятно, так же просто, как не включать myPoly.obj, когда вы связываете свою программу. Я не знаком с Code:: Blocks (хотя я знаю, что он довольно популярен), но я предполагаю, что если вы просто, например, нажмете на test.cpp и выберите "Запустить", то Code:: Blocks попытается создать программу из просто один исходный файл. Вам нужно будет включить все соответствующие исходные файлы в каждую созданную вами программу.

Ответ 4

В дополнение к тому, что говорили другие ребята: вы не делаете наследование правильно...

Когда вы делаете

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

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

Вам нужно объявить дочерний конструктор следующим образом:

Rect::Rect() : Poly()
{

}

Ребенок должен строиться только после того, как отец закончил строительство.