【问题标题】:Using multiple headers and cpp files help使用多个头文件和 cpp 文件帮助
【发布时间】:2011-04-06 22:59:51
【问题描述】:

我正在使用 DragonFireSDK 制作一个应用程序,我想用 .cpp 和 .h 文件组织我的数千行应用程序

我在尝试做某事时遇到大量错误

所以我的 app.cpp (main, required one) 看起来像这样

代码:

#include "DragonFireSDK.h"

#include "SaveData.h"
#include "Structures.h"
#include "Definitions.h"
#include "Variables.h"
#include "SaveData.h"

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "Functions.cpp"
#include "AppMain.cpp"
#include "AppExit.cpp"
#include "OnTimer.cpp"

#include "SaveData.h" 到 #include "Variables.h" 都有类似的东西 代码:

#ifndef _HeaderName
#define _HeaderName
//STUFF HERE LIKE
#define player1 0
#define player2 1
//OR
typedef struct _number {
    int view;
    int number;
    bool able;
    int opacity;
};_number number[4];
//OR
int whoseturn;

int bet[5];
bool reachedmax[5];

int playerimg[5];
#endif

现在我可能已经做错了,但这里还有一些...... 我的 AppMain.cpp、OnTimer.cpp 等看起来像这样 (AppMain() 等也是必需的函数) 代码:

#include "DragonFireSDK.h"
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "Definitions.h"
#include "Structures.h"
#include "Variables.h"
#include "SaveData.h"

#include "Functions.cpp"

void AppMain() {
//STUFF HERE
};

现在这就是我认为问题所在... 函数.cpp 代码:

#include "DragonFireSDK.h"

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "SaveData.h"
#include "Structures.h"
#include "Definitions.h"
#include "Variables.h"
//SOME FUNCTIONS
void   SavePlayerMoney();
void   SetInfo            (int idnum, bool actuallyset = false);
void   SwitchButton       (int idnum, bool makeactive=true);
void   DisableButton      (int idnum);
double round              (double number);

void SavePlayerMoney() {
    //...
}
void   SetInfo(int idnum, bool actuallyset) {
      //...
}
void   SwitchButton(int idnum, bool makeactive) {
      //...
}
void   DisableButton(int idnum){
      //...
}

现在我认为如果修复了所有的东西之后我得到的错误...... 代码:

1>AppMain.obj : error LNK2005: "void __cdecl SwitchButton(int,bool)" (?SwitchButton@@YAXH_N@Z) already defined in App.obj
1>AppMain.obj : error LNK2005: "double __cdecl round(double)" (?round@@YANN@Z) already defined in App.obj
1>AppMain.obj : error LNK2005: "void __cdecl SetInfo(int,bool)" (?SetInfo@@YAXH_N@Z) already defined in App.obj
1>AppMain.obj : error LNK2005: "int __cdecl Digits(int)" (?Digits@@YAHH@Z) already defined in App.obj

非常感谢任何帮助!

【问题讨论】:

  • 避免使用宏和预处理器#define player1 0。不要#include .cpp 文件。避免使用全局变量,更喜欢使用类对象进行封装。实际上,只要有机会,就拿起一本好的 C++ 书。
  • 尽管有 c++ 标签和 *.cpp 文件名,这看起来(从我们所见)是一个纯 C 程序。
  • 缺少一些基本概念。认真考虑获得像 Visual Studio 这样的 IDE 和这样的书:amazon.com/Primer-Plus-5th-Stephen-Prata/dp/0672326973

标签: c++ include compiler-errors linker-errors function-declaration


【解决方案1】:

不要#include .cpp 文件。

C 编译模型是每个函数都定义一次,即恰好在一个编译单元(即一个目标文件)中。您将每个源文件独立编译成一个单独的目标文件(#include-ing header 文件,以便编译器知道例如要使用的函数的原型)。然后将这些单独的目标文件链接在一起以形成最终的可执行文件。

如果你#include .cpp 文件,你最终会在多个编译单元中定义相同的函数(记住#include 基本上相当于将内容复制粘贴到正在执行包含的文件)。因此链接器会感到困惑,并为您提供您所看到的消息。

更新

哦,我知道问题是您没有Functions.cpp 的相应头文件。这个想法是你也写一个Functions.h,大致如下:

#ifndef FUNCTIONS_H_
#define FUNCTIONS_H_

void SavePlayerMoney();
void SetInfo(int idnum, bool actuallyset);
void SwitchButton(int idnum, bool makeactive);
void DisableButton(int idnum);

#endif

然后你#include这个头文件,而不是.cpp文件。

【讨论】:

  • 那我怎么处理它们,它们有app和AppMain.cpp需要的功能
  • 我只是更改扩展名还是什么?
  • @Mark:只有#include 头文件(事实上,只有那些相关的)。至于如何将这一切联系在一起,这取决于您如何构建项目。如果您使用的是 IDE,它通常会为您处理这个问题。如果您正在使用例如GCC 在命令行,你需要像g++ file1.cpp file2.cpp -o my_app 这样的东西。
  • 正如我在下面所说的 - 我在哪里包含 Functions.cpp?
  • @Mark:你不(好吧,你不应该)#include .cpp 文件。您分别编译每一个,然后将它们链接在一起。
【解决方案2】:

链接器抱怨,因为函数被定义了不止一次。一个函数只能在一个翻译单元(cpp 文件,编译后成为 obj 文件)中定义——除非它被声明为inline

您将 Functions.cpp 包含在其他单元中,因此来自 Function.cpp 的函数定义会被复制到这些单元中,从而导致链接器出现问题。

解决方案是声明函数inline - 或者,更好的是,声明它们在标题中(即Functions.h)并在@中定义它们987654326@。这些功能的任何用户都可以#include Functions.h 访问这些功能,即使他们不知道它们的实现。

声明一个函数,请执行:int foo();,要实际定义它,请执行int foo() { your code goes here}.

【讨论】:

  • 好吧听起来可能是这样,但有一个问题......他们有默认值......它会抱怨重新定义默认值
  • 嗯...我会在一秒钟内找出答案:)
  • 其实还有一个问题——我在哪里包含定义它们的Functions.cpp?
  • @用函数原型的声明在头文件中标记你的默认值,然后在你的函数实现的cpp端定义没有= value部分的参数。
  • 您不包括Functions.cpp。它被单独编译。链接器(给你那些讨厌的错误的工具)最终解决了对其他翻译单元中定义的函数的引用。
【解决方案3】:

我认为每个人都回答得很好,所以我只想向您介绍我在大型项目中的 C++ 理念,因为它似乎是您可能会发现有用的信息。

总是将函数声明和实现分开。 这将使您的生活变得更加轻松。在 .h 文件中声明函数原型,然后在 .cpp 文件中编写实现。

例如:

// mystuff.h
#ifndef MYSTUFF_H
#define MYSTUFF_H

int myFunction(int value, char letter);

#endif

在我的 .cpp 文件中:

// mystuff.cpp

#include "mystuff.h"

int myFunction(int value, char letter) {
    // insert implementation here
}

为什么要这样做?一个重要的原因是,当您的代码不起作用时(表面上看来,这是任何程序员都无法回避的现实),您可以用替代实现替换您的 .cpp 文件,而无需修改代码的结构。不仅如此,您还会发现各种依赖于分离声明和实现的技巧,这将大大简化您的生活。归根结底,去做吧。

尽可能尝试封装。 如果你在做一个大项目(你会注意到你遇到的大多数大项目都是如此),封装类似的函数、变量等将节省你大量的时间和精力。看起来你正在编写一个玩游戏的程序——你是否考虑过将每个玩家封装到一个 Player 或 Human 类中,并为每个玩家提供特定于类的函数?如果您像我一样是 C++ 或 Java 迷,您会发现面向对象的方法是最有效的方法,100 次中有 99 次是最有效的方法(1% 的情况通常是您的辅助函数并不真正适合在您定义的任何对象中)。

此外,封装使您能够利用面向对象设计的另外两个基本原则——多态性和继承。例如,您可以定义一个 Player 类,那么如果您的游戏涉及计算机玩家和人类玩家,您可以为每个玩家编写一个单独的类,该类继承 Player 的基本功能,但在一个 Player 中实现 Player 的每个功能不同的方式(即,如果有一个 makeMove 函数,那么人类的实现将不同于计算机的实现。因此,继承极大地简化了您的工作)。 OO 设计显然有许多吸引人的品质,但就我从您的代码中收集到的内容而言,我认为您将从这些品质中受益最多。

显然,这是我自己的哲学,而不是我想强加给你的哲学。但希望你能从我的简短漫谈中得到一些有用的提示,以改进你编写代码的方式和/或避免长长的错误列表。祝你好运!

【讨论】:

  • 虽然这里有一些很好的建议,但这并不是问题的真正答案。
  • 有很好的建议并帮助我了解更多:+1 所以它不是负面的!
【解决方案4】:

将函数声明移至头文件。例如,看起来 Functions.h 应该包含:

#ifndef FUNCTIONS_H
#define FUNCTIONS_H

//SOME FUNCTIONS
void   SavePlayerMoney();
void   SetInfo            (int idnum, bool actuallyset = false);
void   SwitchButton       (int idnum, bool makeactive=true);
void   DisableButton      (int idnum);
double round              (double number);

#endif

那么 Functions.cpp 可以只包含 Functions.h 而不是那些声明。一些头文件可能需要包含其他头文件才能得到合适的类型。

最后,永远不要#include *.cpp 文件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    相关资源
    最近更新 更多