将一个C++文件包含到另一个C++文件中

7

我在包含我的文件时遇到了问题。我有三个C++文件,它们都有int main(void)。

问题是,每当我包含其中一个文件时,它会显示:

function 'int main(void)' already has a body

但如果我将int main(void)从其他两个C++文件中删除,现在就会出现这些错误。

'one or more multiply defined symbols found'

"class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl convertInt(int)" (?convertInt@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z) already defined in FormatPosDataXml().obj

"class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl convertInt(int)" (?convertInt@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z) already defined in FormatPosDataXml().obj    

等等之类的

这是我得到的代码:

FormatPosDataXml().cpp

#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <cstring>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>



using namespace std;

#define nextline '\n'

inline bool TextContains(char *text, char ch) {
  while ( *text ) {
    if ( *text++ == ch )
      return true;
  }

  return false;
}

void Split(char *text, char *delims, vector<string> &words) {
  int beg;
  for (int i = 0; text[i]; ++i) {

    while ( text[i] && TextContains(delims, text[i]) )
      ++i;

    beg = i;

    while ( text[i] && !TextContains(delims, text[i]) )
      ++i;
        words.push_back( string(&text[beg], &text[i]) );
  }
}

string convertInt(int number)
{
   stringstream ss;//create a stringstream
   ss << number;//add number to the stream
   return ss.str();//return a string with the contents of the stream
}


string dateFormatChecker(const char *date){
    string strdate=date;
    char getdate[50];
    strcpy_s(getdate, strdate.c_str());

    vector<string> checkdate;
    Split( getdate, "-", checkdate );
    int year, month, day;
    year=atoi(checkdate[0].c_str());
    month=atoi(checkdate[1].c_str());
    day=atoi(checkdate[2].c_str());

    string checkyear, checkmonth, checkday, checkhour, checkminute, checksecond;

            checkyear = convertInt(year);
            if(month<10){
            checkmonth = "0" + convertInt(month);
            }
            else{
            checkmonth = convertInt(month);
            }
            if(day<10){
            checkday = "0" + convertInt(day);
            }
            else{
            checkday = convertInt(day);
            }

            /*
            cout << checkdate[0] << ' ' << checkyear << '\n'
                 << checkdate[1] << ' ' << checkmonth << '\n'
                 << checkdate[2] << ' ' << checkday << '\n';
            */
            if (checkyear.size() != checkdate[0].size()||
                checkmonth.size() != checkdate[1].size()||
                checkday.size() != checkdate[2].size()){
                return "";
            }
    return date;
}

string dateandtimeFormatChecker(const char *dateandtime){
        string strdate=dateandtime;
        char getdateandtime[50];
        strcpy_s(getdateandtime, strdate.c_str());

        vector<string> checkdateandtime;
            Split( getdateandtime, "-: ", checkdateandtime );
        int year, month, day, hour, minute, second;
            year=atoi(checkdateandtime[0].c_str());
            month=atoi(checkdateandtime[1].c_str());
            day=atoi(checkdateandtime[2].c_str());
            hour=atoi(checkdateandtime[3].c_str());
            minute=atoi(checkdateandtime[4].c_str());
            second=atoi(checkdateandtime[5].c_str());

            string checkyear, checkmonth, checkday, checkhour, checkminute, checksecond;

            checkyear = convertInt(year);
            if(month<10){
            checkmonth = "0" + convertInt(month);
            }
            else{
            checkmonth = convertInt(month);
            }
            if(day<10){
            checkday = "0" + convertInt(day);
            }
            else{
            checkday = convertInt(day);
            }
            if(hour<10){
            checkhour = "0" + convertInt(hour);
            }
            else{
            checkhour = convertInt(hour);
            }
            if(minute<10){
            checkminute = "0" + convertInt(minute);
            }
            else{
            checkminute = convertInt(minute);
            }
            if(second<10){
            checksecond = "0" + convertInt(second);
            }
            else{
            checksecond = convertInt(second);
            }


            if (checkyear.size() != checkdateandtime[0].size()||
                checkmonth.size() != checkdateandtime[1].size()||
                checkday.size() != checkdateandtime[2].size()||
                checkhour.size() != checkdateandtime[3].size()||
                checkminute.size() != checkdateandtime[4].size()||
                checksecond.size() != checkdateandtime[5].size()){
                return "";
            }

        //cout << year<< '/' << month << '/' << day << ' ' << hour << ':' << minute << ':' << second << '\n';

        return dateandtime;     
}

string transaction (const char * SequenceNumber, const char * RetailStoreID, const char * WorkStationID, const char * BusinessDayDate, const char * BeginDateTime, const char * StartTransTime, const char * EndTransTime, const char * EndDateTime, const char * RawData){

    string output;

    string bdd, bdt, stt, ett, edt;

    bdd = dateFormatChecker(BusinessDayDate);
    bdt = dateandtimeFormatChecker(BeginDateTime);
    stt = dateandtimeFormatChecker(StartTransTime);
    ett = dateandtimeFormatChecker(EndTransTime);
    edt = dateandtimeFormatChecker(EndDateTime);


    cout << "<Transaction>" << "\n\t<RetailStoreID>"
         << RetailStoreID   << "</RetailStoreID>\n\t<WorkStationID>"
         << WorkStationID   << "</WorkStationID>\n\t<SequenceNumber>"
         << SequenceNumber  << "</SequenceNumber>\n\t<BusinessDayDate>"
         << bdd             << "</BusinessDayDate>\n\t<BeginDateTime>"
         << bdt             << "</BeginDateTime>\n\t<StartTransTime>"
         << stt             << "</StartTransTime>\n\t<EndTransTime>"
         << ett             << "</EndTransTime>\n\t<EndDateTime>"
         << edt             << "</EndDateTime>\n\t<RawData>"
         << RawData         << "</RawData>\n</Transaction>";

    output = _getch();
    return output; 
}

int main(void) {
  vector<string> words;
  char * data = "1,1,SAMPLE,2010-01-31,2011-01-31 14:09:10,2011-01-31 14:42:10,2011-01-31 14:42:10,2011-01-31 14:42:10,JELLY-O RUBBERB\n\r               13.25V.¶üÁËO";

  Split( data, ",", words );

  char SN[11], RSI[200], WSI[200], BDD[100], BDT[100], STT[100], ETT[100], EDT[100], RD[100];

  strcpy_s(SN, words[0].c_str());
  strcpy_s(RSI, words[1].c_str());
  strcpy_s(WSI, words[2].c_str()); 
  strcpy_s(BDD, words[3].c_str());
  strcpy_s(BDT, words[4].c_str());
  strcpy_s(STT, words[5].c_str());
  strcpy_s(ETT, words[6].c_str());
  strcpy_s(EDT, words[7].c_str());
  strcpy_s(RD, words[8].c_str());

  string PosData;
  PosData = transaction(SN,RSI,WSI,BDD,BDT,STT,ETT,EDT,RD);


/* Checker 
  for (int i = 0; i != words.size(); i++){
      cout << words[i] << nextline;
  }
    cout << SN << nextline << RSI << nextline << WSI << nextline << BDD << nextline << BDT << nextline << STT << nextline << ETT << nextline << EDT << nextline << RD; 
*/
return 0;
}

FSNPC.cpp

#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <cstring>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include "FormatPosDataXml().cpp"

using namespace std;


string getstring(string holder){
    if (holder == "" || holder.size()>100){
        exit(1);
    }

    int i=0,ch=0;
    int size;
    char charInput[100];

        strcpy_s(charInput, holder.c_str());
        size = strlen(charInput);

        #define DATA_LENGTH 100
        #define BUFFER_LENGTH (DATA_LENGTH)

        char Buffer[BUFFER_LENGTH];

            while (i < DATA_LENGTH) {
                Buffer[i++] = charInput[i];
                Buffer[i] = '\0';
                if(size == i){
                        break;
                    }
            }
            holder = Buffer;

            strcpy_s(charInput, holder.c_str());
            size = strlen(charInput);

            i = 0;
            for(int j = 0;j<size;j++)
            {
                if (charInput[j] < 2) 
                {
                    if (charInput[j+i] > 2 && charInput[j+i] != 17){
                        charInput[j] = charInput[j+i];
                        charInput[j+i]='\0';
                        i=0;
                        }
                    else{
                        i++;
                        j--;
                    }
                }else if (charInput[j] == 17) 
                {
                    if (charInput[j+i] > 2 && charInput[j+i] != 17){
                        charInput[j] = charInput[j+i];
                        charInput[j+i]='\0';
                        i=0;
                        }
                    else{
                        i++;
                        j--;
                    }
                }
            }
            size = strlen(charInput);
            for(int remove = 0; remove<size ;remove++)
            {
                if (charInput[remove] < 2 || charInput[remove] == 17) 
                {
                    charInput[remove]='\0';
                }
            }

             string handler;
             handler = charInput;

             handler = handler.substr(0, handler.length() - 1);
             return (handler);
    }



/*
int main(void){

    string final;


    string input = "JELLY-O RUBBERB\n\r               13.25V.¶üÁË0";
    string input2 = "STIÁËCK-O CHOCO\n\r               10.52C.ÁË0¶ü";
    string input3 = "STICÁËK-O VANILLA\n\r               10.52C.ÁË0¶ü";


      final = getstring(input)+ "\n" +getstring(input2)+ "\n"
+getstring(input3);
        cout<<final;
        _getch();


    return 0;

}*/

keypress.cpp

#include <iostream>
#include <conio.h>
#include <stdio.h>
#include <string>
#include "FormatPosDataXml().cpp"

    using namespace std;

int c;
char temp[256];

    char getkeypress(char c){

        if (c==0x1b){
            exit(1);
         }

         else if (c==0||c==224)        
          {
                    c = _getch();

                    if (c==0x3b){
                        cout << "You typed: F1\n";
                    }
                    else if(c==0x3c){

                        cout << "You typed: F2\n";
                    }
                    else if(c==0x3d){
                        cout << "You typed: F3\n";
                    }
                    else if(c==0x3e){
                        cout << "You typed: F4\n";
                    }
                    else if(c==0x3f){
                        cout << "You typed: F5\n";
                    }
                    else if(c==0x40){
                        cout << "You typed: F6\n";
                    }
                    else if(c==0x41){
                        cout << "You typed: F7\n";
                    }
                    else if(c==0x42){
                        cout << "You typed: F8\n";
                    }
                    else if(c==0x43){
                        cout << "You typed: F9\n";
                    }
                    else if(c==0x44){
                        cout << "You typed: F10\n";
                    }
                    else if(c==133){
                        cout << "You typed: F11\n";
                    }
                    else if(c==134){
                        cout << "You typed: F12\n";
                    }
          }

          else
          { 
                while((cin.getline(temp, sizeof(temp), '\n'))&&(temp!="")){
                    cout << "You typed:" << temp << '\n';
                    break;
                }
          }

    }
/*  
int main(void){



    while (c^=0x1b){
            cout<<"Press any key:\n";
            c = getkeypress(_getch());
    } 
      _getch();
      return 0;
}
*/

我如何链接这些文件呢?我希望它们成为一个库。那么我该怎么做呢?

5
这段代码太难读了。也许你或其他人可以更好地格式化它。另外,在提问中不应该发布完整的代码示例。尝试将其裁剪到生成错误所需的最少代码量。 - mgiuca
1
为什么要将一个cpp文件包含到另一个文件中?你想要实现什么目标(抽象层面上的,暂时不考虑代码)? - Dave O.
@Andro,请接受我的编辑。你的代码将会更易读。 - mgiuca
@mgiuca:你应该在问题中发布完整的代码示例!在发布之前,你还应该将测试用例缩小到可管理的范围。(我已经对你的“编辑建议”进行了“投票”,但在它被批准(需要更多的投票)或被拒绝之前,我无法进行编辑;我对这个系统不太熟悉。) - Fred Nurk
@Fred Nurk:我猜我的投票是为了“把它推上去”,可以这么说。不幸的是,虽然代码现在格式更好了(感谢@mgiuca),但它仍然只是一大堆代码,没有任何告诉我们他真正想要的东西... - Jerry Coffin
显示剩余2条评论
5个回答

12

通常将另一个.cpp文件包含在.cpp文件中是不良实践。正确的方法是将声明拆分到.h文件中并将定义放入.cpp文件中。确保在每个.h文件的顶部放置一个虚假的定义,以防止意外重新包含文件,例如:

#ifndef MYFILE_H_
#define MYFILE_H_

// your code goes here

#endif

当你编译你的程序时,你需要将所有的.cpp文件编译成.o文件(在Windows上为.obj),然后再将它们链接起来。例如(Linux/Mac):

g++ -c foo.cpp
g++ -c bar.cpp
g++ foo.o bar.o -o theMainExecutable

3
_MYFILE_H_是一个保留的名称,不要使用它。 - Fred Nurk
1
好的,修改了。请参考https://dev59.com/KHVC5IYBdhLWcg3woSxW 了解原因。 - EmeryBerger

3
我没有阅读所有的代码,但我猜测您在两个模块中定义了相同的函数。首先,您不应该在三个文件中都有一个main。只需要一个文件有。如果您计划制作一个库,则这些文件中都不应该有main -- 应该放在导入库的程序中。
至于convertInt,我怀疑它在多个文件中被定义。如果不是这样,那么它可能是在头文件中定义的。您只应该在头文件中放置声明,例如:
string convertInt(int number);

(注意分号)

定义应该总是出现在相应的CPP文件中,就像这样:

string convertInt(int number) { ... // Body of the function }

如果定义出现在头文件中,那么会有问题,因为每个包含该头文件的CPP文件都将包含重复的定义。


啊,看了Chris的回答,我明白问题所在了:你正在#include一个CPP文件,实际上导致它被用作头文件。 - mgiuca
定义可能出现在标题中,最常见的是作为内联。 - user1481860
@Øystein 嗯,只能作为内联(这是避免错误的唯一方法)。我试图为初学者提供一些开始学习C++的基本规则,而不是解释语言的所有怪癖。 - mgiuca
我认为你所说的“most commonly”实际上就是“always”。 - tenfour
@mgiuca:我从未向OP提供任何建议。作为一名程序员,我自然会对不准确的信息做出反应,并且不愿意看到虚假的事实。我认为这个社区之所以好,部分原因在于这里进行的严谨和精确的讨论,以及对简洁和准确性的关注。这教会了我很多东西。 - user1481860
显示剩余4条评论

3

你不能包含 .cpp 文件,需要使用 .h 文件,这些文件是函数的定义。在头文件中定义函数,然后在 .cpp 文件中实现它们(编写代码)。

int main() 只能存在于一个 .cpp 文件中,你需要将代码放入一个函数中,在头文件中定义它,然后将其包含到主文件中,在 int main() 中执行。


1
@Øystein 当然可以这样做,但是a)这违反了过去30年的所有惯例和已建立的实践,b)导入包含项目多个模块中任何函数定义的文件是非常不合法的。 - mgiuca
1
@mgiuca 内联函数(包括模板)在头文件中非常常见。换句话说,“函数体”并不是区分应该放在.cpp和.h中的内容,而更多的是关于生成的代码应该在哪里 - 内联或链接。 - tenfour
@Chris C/C++ #include 只是将该文件的内容复制并粘贴到您现在所在的文件中。这只是由于链接器不同而已。例如,在Lua中,您确实希望粘贴文件的全部内容,因为顶级模块与后来的其他模块没有链接。在C/C++中,您不能这样做,因为您将稍后单独链接所有定义文件。 - mgiuca
@Fred:嗯?去拿标准 - user1481860
@Øystein:我也在寻找一个要求,明确规定文件不能以部分行(缺少终止换行符)结尾,但我没有在C++标准中看到它;也许它在C语言中有规定。 - Fred Nurk
显示剩余13条评论

2
没有深入研究您的大量代码,似乎您正在尝试将不同的源文件合并到同一项目中,导致名称冲突。
我认为您只需要找到一种方式来组织您的代码,以便您不会得到冲突的符号。显然,您不能在应用程序中拥有三个不同的入口点。您想要哪一个被调用?例如,如果您希望用户进行选择,则必须编写相应的代码,并相应地命名函数。
例如,对于convertInt,如果您需要在应用程序中包含不同变体的此函数,则需要适当命名以加以区分。 convertInt的可能解决方案包括:
  1. convertInt(和可能的其他函数)包装在命名空间中,并像这样调用它们:Variation1 :: convertInt(x)
  2. 只需给予不同的名称。 convertInt1convertInt2等。
  3. 花时间合并相关函数。我怀疑在同一项目中具有相同签名的两个名为convertInt的函数实际上执行不同的操作。将它们合并,以便只有一个要考虑的函数。
如果我的回答与您的问题无关,则请原谅。

1
他并不是试图定义不同版本的 convertInt。相反,只有一个版本,但它被 #include 到了所有三个文件中。 - mgiuca

1

我使用的技术是从其他文件中导入类和变量:(据我所知)

  • 导入原始C++文件。 [虽然不是最优选方法。]

假设我有两个C++文件:

file_1.cpp

#include<iostream>
using namespace std;

int x = 8;            // A Random Variable

class Math{           // A Random Class

    public:

        static int multiplyBy5(int x){
            return x * 5;
};

file_2.cpp [假设是驱动文件]

#include<iostream>
#include "file_2.cpp"                // This can be the Relative/Absolute Path.
using namespace std;

int main()
{
    // Importing External Variable present in the imported File. [Tho we don't need to define `extern` as we're importing the whole file itself.]
    extern int x;                    
    cout<<x<<endl;
    cout<<Math::multiplyBy5(5)<<endl;
    return 0;
}

注意:

  1. main() 方法应该只存在于驱动程序中。在其他文件中定义 main() 方法将会破坏您的驱动代码。
  2. 您可以将要导入的文件的扩展名 .cpp 替换为 .h [接口],这被认为是比直接导入原始 .cpp 更好的做法。
  3. 这样做,您就不需要编译您要导入的其他文件。因此,您可以通过简单地运行驱动程序代码来访问和运行所有文件。
g++ file_2.cpp -o file_2
./file_2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接