即使使用 include guard 也会出现多重定义错误

Getting multiple definition error even with include guard

我的任务是在将代码分解为多个文件时保持循环依赖,代码是关于使用面向对象的台球模拟,但我的问题不是代码而是编译。

我已经解决了这个问题,并使用这个 post 解决了我的问题:Resolve build errors due to circular dependency amongst classes

但是在“make”命令说我已经多次定义某些东西后,我一直收到编译错误;这是在我使用 include guards 防止程序多次包含定义时,我不知何故觉得我在某处犯了一个小错误,但我似乎没有找到它。

这些是我的文件

ball.cpp

#ifndef BALL_CPP
#define BALL_CPP
#include "ball.hpp"
#include <iostream>
using namespace std;
Ball::Ball(double _x, double _y, double _vx, double _vy, Table *t)
{
     cout << "Ball constructor called" << endl;
     table = t;
     set_location(_x, _y);
     set_speed(_vx, _vy);
     cout << "Initial position is: (" << x << ',' << y << ')' << endl;
}
void Ball::set_location(double _x, double _y)
{
     cout << "Ball::set_location called with (" << _x << ',' << _y << ')' << endl;
     if (!table->contains_point(_x, _y))
          abort();
     x = _x;
     y = _y;
}
void Ball::set_speed(double _vx, double _vy)
{
     cout << "Ball::set_speed called with (" << _vx << ',' << _vy << ')' << endl;
     vx = _vx;
     vy = _vy;
}

void Ball::move(double dt)
{
     cout << "Ball::move called" << endl;
     x += vx * dt;
     y += vy * dt;

     if (!table->contains_point(x, y))
          table->reflect(this);
     cout << "New position is: (" << x << ',' << y << ')' << endl;
}
#endif

ball.hpp


#ifndef BALL_HPP
#define BALL_HPP
#include "table.hpp"
class Ball
{
public:
     Ball(double _x, double _y, double _vx, double _vy, Table *t);
     void move(double dt);
     double get_x() { return x; }
     double get_y() { return y; }
     double get_vx() { return vx; }
     double get_vy() { return vy; }
     void set_location(double _x, double _y);
     void set_speed(double _vx, double _vy);

private:
     double x;
     double y;
     double vx;
     double vy;
     Table *table;
};
#endif

table.cpp:

#ifndef TABLE1CPP
#define TABLE1CPP
using namespace std;
#include <iostream>
#include "ball.hpp"
Table::Table(double w, double h)
{
     cout << "Table constructor called" << endl;
     if (w <= 0 || h <= 0)
          abort();
     width = w;
     height = h;
}
bool Table::contains_point(double x, double y)
{
     cout << "Table::contains_point called" << endl;
     return x >= 0 && x < width && y >= 0 && y < height;
}
void Table::reflect(Ball *b)
{
     cout << "Table::reflect called" << endl;
     double x = b->get_x();
     double y = b->get_y();
     double vx = b->get_vx();
     double vy = b->get_vy();

     while (!contains_point(x, y))
     {
          if (x < 0)
          {
               x = -x;
               vx = -vx;
          }
          if (x >= width)
          {
               x = 2 * width - x;
               vx = -vx;
          }
          if (y < 0)
          {
               y = -y;
               vy = -vy;
          }
          if (y >= height)
          {
               y = 2 * height - y;
               vy = -vy;
          }
     }
     b->set_location(x, y);
     b->set_speed(vx, vy);
}
#endif

table.hpp:

#ifndef TABLE_HPP
#define TABLE_HPP
class Ball;
class Table
{
public:
     Table(double w, double h);
     bool contains_point(double x, double y);
     void reflect(Ball *b);

private:
     double width;
     double height;
};
#endif

我的 makefile 看起来像这样 :(我对多文件概念很陌生而且经验不足,所以我可能在这里犯了一个我不明白的错误)

生成文件:

output: main.o table.o ball.o
    g++ main.o ball.o table.o -o billiards
main.o: main.cpp
    g++ -c main.cpp
ball1.o:    ball.cpp ball.hpp
    g++ -c ball1.cpp
table1.o: table.cpp table.hpp
    g++ -c table.cpp
clean:
    rm * .o output

main.cpp :

#include <iostream>
#include <cstdlib>
#include "ball.cpp" // should be changed to "ball.hpp"
#include "table.cpp" // should be changed to "table.hpp"
using namespace std;
int main()
{
     Table t(100, 50);
     Ball b(10, 20, 25, 5, &t);
     b.move(10);
}



错误:


/usr/bin/ld: ball.o: in function `Ball::Ball(double, double, double, double, Table*)':
ball.cpp:(.text+0x0): multiple definition of `Ball::Ball(double, double, double, double, Table*)'; main.o:main.cpp:(.text+0x0): first defined here
/usr/bin/ld: ball.o: in function `Ball::set_location(double, double)':
ball.cpp:(.text+0x10c): multiple definition of `Ball::set_location(double, double)'; main.o:main.cpp:(.text+0x10c): first defined here
/usr/bin/ld: ball.o: in function `Ball::set_speed(double, double)':
ball.cpp:(.text+0x1dc): multiple definition of `Ball::set_speed(double, double)'; main.o:main.cpp:(.text+0x1dc): first defined here
/usr/bin/ld: ball.o: in function `Ball::Ball(double, double, double, double, Table*)':
ball.cpp:(.text+0x0): multiple definition of `Ball::Ball(double, double, double, double, Table*)'; main.o:main.cpp:(.text+0x0): first defined here
/usr/bin/ld: ball.o: in function `Ball::move(double)':
ball.cpp:(.text+0x280): multiple definition of `Ball::move(double)'; main.o:main.cpp:(.text+0x280): first defined here
/usr/bin/ld: table.o: in function `Table::Table(double, double)':
table.cpp:(.text+0x0): multiple definition of `Table::Table(double, double)'; main.o:main.cpp:(.text+0x3be): first defined here
/usr/bin/ld: table.o: in function `Table::Table(double, double)':
table.cpp:(.text+0x0): multiple definition of `Table::Table(double, double)'; main.o:main.cpp:(.text+0x3be): first defined here
/usr/bin/ld: table.o: in function `Table::contains_point(double, double)':
table.cpp:(.text+0x7c): multiple definition of `Table::contains_point(double, double)'; main.o:main.cpp:(.text+0x43a): first defined here
/usr/bin/ld: table.o: in function `Table::reflect(Ball*)':
table.cpp:(.text+0x10a): multiple definition of `Table::reflect(Ball*)'; main.o:main.cpp:(.text+0x4c8): first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:2: output] Error 1

好的,所以,这就是发生的事情:main.cpp 正在拉取 ball.cpptable.cpp,因此目标文件 main.o 将具有所有的完整实现您在 ball.cpptable.cpp 中实现的函数和变量。 这是一个问题,因为您还 单独 编译 ball.cpptable.cpp,所以您最终会得到所有重复项。

我猜你在 .cpp 文件中放置了 include guards 来避免这个问题,然而,这实际上不起作用:#define'd 宏不会在编译器。

您在这里要做的是 #include .hpp 文件:main.cpp 将能够看到您正在使用的功能的声明,但是 它不会将实现编译成 main.o,因此不会与 ball.otable.o 发生任何冲突。 此外,您可能应该从 .cpp 文件中删除 include guards:正是由于这些重复的原因,#includeing C 或 C++ 源文件通常不会完成,除非在一些不真实的奇怪情况下与您的具体情况相关。