使用多个文件制作和编译C ++项目

时间:2015-05-05 23:03:42

标签: c++ g++

我是C / C ++的新手,我已经掌握了基础知识,现在我开始学习更多高级概念。

目前我正在使用C ++开发一个项目,这是一个非常大的项目,因此构建我的项目会更好。

从我已经看到的情况来看,一个好的结构至少依赖于文件夹:/src/include。所有.cpp文件都应放在/src文件夹中,而{.1}}放在.hpp文件夹中。这里出现了第一个疑问:如何包含一个不在同一目录或标准目录中的标题?在搜索之后,我得出结论,最好的方法是将选项/include传递给编译器。

我有三个源文件:main.cpp,GraphData.cpp和RandomGraphGenerator.cpp。我已经读过,最好的做法是为每个文件都有一个标题,将声明与定义分开。现在我也有两个标题,但我没有得到如何编译所有这些。

我正在集中使GraphData.cpp正确链接到main.cpp,我评论了需要RandomGraphGenerator.cpp的所有东西。这是我的主要文件:

-I../include

我的GraphData标题:

// main.cpp
#include <iostream>
#include <string>
#include <Snap.h>
#include <GraphData.hpp>

int main() {
    std::string file_path;
    char path[1024];
    DataPoint **data_set;
    int motif_size, num_null_models;

    std::cin >> file_path;
    std::cin >> motif_size;
    std::cin >> num_null_models;

    data_set = new DataPoint*[num_null_models];

    strncpy(path, file_path.c_str(), sizeof(path));
    path[sizeof(path) - 1] = 0;

    //read input
    PNGraph G = TSnap::LoadEdgeList<PNGraph>(path, 0, 1);

    //enumerate and classify k-subgraphs in G
    GD::extractData(&G, motif_size, data_set[0]);

    //generate random graphs and enumerate and calssify k-subgraphs on them
    for(int i=1; i<=num_null_models; i++) {
        //GM::randomize(&G);
        GD::extractData(&G, motif_size, data_set[i]);
    }

    //detect motifs
    GD::discoverMotifs(data_set);

    return 0;
}

我的GraphData文件:

#ifndef GRAPHDATA_HPP_INCLUDED
#define GRAPHDATA_HPP_INCLUDED

#include <Snap.h>

struct DataPoint;

namespace GD {
    void extractData(PNGraph*, int, DataPoint*);
    DataPoint* discoverMotifs(DataPoint**);
}

#endif // GRAPHDATA_HPP_INCLUDED

我正在尝试使用GCC 4.9.2在Ubuntu 14.10下编译,使用:

#include <GraphData.hpp>

struct DataPoint {
    int label;
    int frequency = 0;
};

void GD::extractData(PNGraph* G, int k, DataPoint* data_array) {
    //stuff
}

DataPoint* GD::discoverMotifs(DataPoint** data_set) {
    //dummy code
    DataPoint* dp;
    dp = new DataPoint[2];
    dp[0].label = 10;
    dp[1].label = 99;
    return dp;
}

但它给了我一个未定义的引用错误:

g++ -o main main.cpp /usr/include/Snap-2.3/snap-core/Snap.o -I../include -I/usr/include/Snap-2.3/snap-core -I/usr/include/Snap-2.3/glib-core

我有点失落,我错过了什么?

2 个答案:

答案 0 :(得分:2)

g ++默认在编译完所有输入文件后立即调用链接器(ld)。有许多方法可以生成二进制可执行文件,但最常见的两种方法是:

  1. 在单个g++命令中包含所有源文件。您需要将GraphData.cppmain.cpp添加到同一个g++的来电中。 此方法可确保链接器可以访问所有符号以创建可执行文件。
  2. 将每个文件分别编译为目标文件,并在单独的步骤中链接它们(这对于大型软件项目来说更常见)。 为此,请为每个cpp文件将-c开关传递给g++,然后再次调用g ++并传递.o个文件而不是.cpp个文件。
  3. 无论哪种方式,在某些时候,您必须立即将所有符号提供给链接器,以便它可以创建可执行文件。

    正如Joachim解释的那样,除了几个文件之外的任何事情都会变得非常痛苦,此时学习构建系统可能非常有利可图。他提到了make,但我也会考虑研究cmake,它具有更简单的语法并且是跨平台的。它甚至可以自动为您生成Makefiles

答案 1 :(得分:1)

[注意:这不是问题的答案,它只是解释如何逐个编译多个文件]

可以单独编译源文件,但是你必须告诉g++创建目标文件,然后将它们链接在一起。

这样的事情:

$ g++ -Wall -g -c -I../include -I/usr/include/Snap-2.3/snap-core -I/usr/include/Snap-2.3/glib-core main.cpp
$ g++ -Wall -g -c -I../include -I/usr/include/Snap-2.3/snap-core -I/usr/include/Snap-2.3/glib-core GraphData.cpp
$ g++ -g  main.o GraphData.o /usr/include/Snap-2.3/snap-core/Snap.o -o main

选项-Wall告诉g++启用更多警告,这很好,因为警告是您可能做错的迹象(如导致source code)。 -g选项告诉g++生成调试信息,在开发时总是很好,以防您需要在调试器中运行程序。最后,-c选项告诉g++生成目标文件而不是可执行程序。

然后你有一个单独的命令将目标文件链接在一起形成最终的可执行文件。

如果您获得的文件不止一些,这一切都会成为背后的痛苦,那么您应该了解undefined behavior这个程序可以为您自动完成大部分工作。例如,它会检查文件上的时间戳,以确保您不编译未修改的文件。