使用#include和Inclusion Guards的独立文件中的C++继承

C++ Inheritance in Separate Files Using #include and Inclusion Guards

本文关键字:文件 C++ 继承 独立 使用 Inclusion Guards #include      更新时间:2023-10-16

我是Stack Overflow的新手,正在自学C++,但我还是个初学者。在完成了我正在使用的书的一大部分(这本书可能被认为是过时的和/或不是一本伟大的书)后,我决定通过自己尝试来重新实施一些概念,只有在需要时才参考这本书,但我似乎被卡住了。我试图解决的概念是继承、多态性、抽象数据类型(ADT),以及将类的代码分离为头文件(.h)和C++文件(.cpp)。很抱歉提前出现了文本墙,我只想明确和具体地说明我需要在哪里。

因此,我的目标是创建简单的形状类,在适用的情况下相互继承。我有四个类:myPoly、myRectangle、myTriangle和mySquare。如果我正确理解了这个概念,myPoly应该是一个ADT,因为其中一个方法是纯虚拟函数(面积方法),因为创建myPoly对象不是我希望类的用户做的事情。myRectangle和myTriangle都派生自myPoly,而mySquare又派生自myRectanger。我还包括了我的测试程序,我计划在其中测试我的课程。我正在使用代码::Blocks 10.05,当我构建test.cpp程序时,不断出现以下错误:

undefined reference to 'myPoly::myPoly()'

对于myPoly类的方法,我得到了42个类似的错误。当我尝试为myRectangle和myTriangle构建.cpp文件时,也会发生这种情况。通过我试图对我在这个小项目中遇到的问题进行的研究,我觉得我的包容性保护或#include语句出了问题,有些内容没有被正确地包含或被包含太多次。起初,我将myPoly的.cpp文件提供给myRectangle和myTriangle,但在一些地方读到,为myPoly包含.h文件更有效,以及如何自动包含其.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()
{
}

测试.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;
}

我很好奇为什么我会出现这些错误,或者为什么我所做的事情可能不被认为是好的/最佳实践,而不是仅仅接收一行代码来消除我的错误。

您的包含保护看起来不错。如果不是,您很可能会得到一个编译器错误,包括文件和行号信息。您发布的错误看起来更像是链接器错误。

但是,您的代码有一个"问题"。一般来说,应该只使用#include.h文件,而不使用.cpp文件。

现在来解决问题:我不熟悉Code::Blocks本身。然而,我希望我能提供一些一般性的信息,为你指明正确的方向。默认情况下,我过去使用过的一些编译器允许我编译一个C++文件并运行程序。要用多个文件编译一个程序,我必须创建一个项目。(大多数现代编译器都要求您从一开始就创建项目。)考虑到这一点,我建议您查看如何在代码块中为程序创建项目。

从代码的角度来看(至少我看了一下),它看起来很好,但是:

有两件事需要考虑:

  1. 不要直接包含cpp文件。例如,在mySquare.h中,#include "myRectangle.cpp"应该是#include "myRectangle.h"。您希望包含头文件中提供的接口/声明,这些接口/声明告诉程序如何生成类,而不仅仅是函数定义。

  2. 第二,确保编译时使用了所有的对象文件。我不知道代码块,但如果你使用g++或类似的东西,你会想对所有文件执行g++ main.cpp myPoly.cpp mySquare.cpp etc.。例如,如果您忘记myPoly.cpp,可能会发生这样的错误,因为它的函数没有定义。

实际上,一切看起来都很好。当你链接你的程序时,它可能就像不包括myPoly.obj一样简单。我不熟悉Code::Blocks(尽管我知道它相当流行),但我认为,例如,如果你点击test.cpp并选择"运行",Code::Block将尝试仅从一个源文件构建程序。您需要在构建的每个程序中包含所有相关的源文件。

除了otehr的人说的:你没有做继承权。。。

当你做

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

您需要以以下方式声明子级的构造函数:

Rect::Rect() : Poly()
{
}

孩子必须在父亲完成建造后才能建造。