繁体   English   中英

如何从另一个 .cpp 文件中的一个 .cpp 文件调用函数?

[英]How can I call functions from one .cpp file in another .cpp file?

我尝试查找此内容并使用头文件等得到混合结果。

基本上,我有多个 .cpp 文件,其中包含我为使用二叉树、 BST 、链表等而制作的所有函数。

我不想复制和粘贴我需要的函数,我只想能够做一个:

#include <myBSTFunctions.h>

并且能够调用和使用我自己的函数。

有哪些步骤来实现这一点? 用我使用的所有函数原型制作一个头文件?

我应该将所有实际功能的 .cpp 和头文件放在哪里?

有没有办法直接调用函数文件的目录?

即,我更想将它与主要源 .cpp 文件放在同一个文件夹中,以便与我的一些同事共享。

我怎样才能做到这一点?

在 Windows 和MinGW编译器上。

在标头中声明函数

//MyFunctions.h
int myFunction1(int,int);
int myFunction2(int,int);

在MyFunctions.cpp中实现它们

//MyFunctions.cpp
#include "MyFunctions.h"
int myFunction1(int a, int b){
    //your code
}
int myFunction2(int a, int b){
    //your code
}

将标头包含在所需的任何文件中

//OtherFile.cpp
#include "MyFunctions.h"
//Now you have access to the functions defined in MyFunctions.h in this file

我不知道miniGW,但是它应该看起来像g ++ otherfile.cpp MyFunctions.cpp ...

您似乎在构建程序方面缺少一些非常基本的概念。 我将为您提供一个非常基本的入门,但是您不得不在其他地方找到更完整的答案,才能真正了解正在发生的事情并获得设置的详细信息。

  1. 通常,您告诉编译器编译每个cpp文件。 当cpp文件具有#include语句时,该语句基本上将include d文件复制并粘贴到cpp文件中,然后再进行编译(这由预处理程序完成)。 编译器处理的每个完整单元(包含附件的cpp文件)都称为转换单元 每个翻译单元产生一个目标文件

  2. 目标文件包含已编译的代码,但是它们通常不完整。 也就是说,它们包含对其中未包含的代码的引用。 基本上,他们可以说“现在调用此函数,我不知道它在哪里或在做什么,但是您应该调用它”。

  3. 然后使用链接将目标文件(可能与库一起)链接到可执行文件(或库)中。 链接器的工作是通过在其他目标文件和库中查找相关代码来“解析”对每个目标文件中外部代码的所有引用。

  4. 库有两种类型: 共享库(Windows中为.dll )和静态库。 静态库通过链接器链接到可执行文件(或其他库)中,这意味着您可以在不使用该库的情况下使用该可执行文件(相关的库代码将成为可执行文件的一部分)。 您还可以将可执行文件/库链接到共享库,在这种情况下,每次运行可执行文件时都需要该共享库的副本-操作系统需要先将编译后的代码动态链接到共享库,运行它。

所以,回到您的问题。

大致来说,您有三种选择:编译然后每次都在一个项目中直接链接所有cpp文件; 将有用的可重用代码编译到静态库中,然后将您的项目链接到该库; 或将有用的可重用代码编译到共享库中,将您的项目链接到该共享库,并确保将结果随共享库一起提供,以便可以运行它。

任何合理规模的项目都将其中至少两个结合起来。 多个cpp文件将成为项目代码的一部分,这些文件将被编译为单独的翻译单元,并提供给链接器。 大多数项目还会使用某些库(您自己编写或其他人编写的),这些库会适当地进行静态或动态链接。

不幸的是,(imho)C ++作为一种语言并没有一个单独的构建系统来为您组织所有这些(最近的语言经常这样做)。 有几种不同的编译器/链接器以及许多不同的构建系统都可以完成所有这些工作。 不幸的是,您需要采取的具体步骤很大程度上取决于您选择的编译器和构建系统。

正如JMAA所说的那样,这很简单,您应该进行一些研究以理解这些概念,但是要实际操作,这是您会做的:

您将定义一个functionExample.cpp ,其中您必须定义所有函数,以及一个functionExample.h ,您将在其中声明您的函数。

您可以将其作为functionsExample.cpp

#include <iostream>
int example(int x, int y)
{
    return x + y;
}

将此作为functionsExample.h

#ifndef FUNCTIONSEXAMPLE_H
#define FUNCTIONSEXAMPLE_H

int example(int x, int y);

#endif 

然后在cpp中要运行示例函数,只需添加:

#include "functionsExample.h"

但是正如我所说,您应该对标头防护,预处理器指令和文件组织进行一些研究,以对此有更深入的了解。 我会建议一些链接:

头文件

预处理程序指令

最简单的方法是使用构建工具。 我个人比较喜欢介子构建,但是CMake更受欢迎。 还有其他一些,但是您不会错。 两者都是跨平台的,并且支持不同的工具链。 这意味着只要您的代码是标准C ++,您就可以使用MinGW,Visual Studio,G ++或Clang轻松地对其进行编译,而无需进行任何更改。 他们让您从平台上可用的工具链中进行选择。 每个网站上都有一个快速入门教程。

您编写的配置文件指定要使用的程序源文件以及要构建的可执行文件。 在配置步骤中选择了您使用的构建链,然后,您可以通过运行make或ninja(由meson要求,建议用于CMake)来构建可执行文件。 如果您更改来源,则只需重新运行make或ninja。 当然,只有已更改的零件才能重建。 也仅重新链接受更改影响的可执行文件。

观看一些详细的构建以熟悉系统上的构建过程可能是有教益的。

ps传统上,在#includes中,对系统标题使用尖括号(<>),对系统标题使用引号(“”)。 它与首先寻找它们的位置有关。

麦克风

这篇文章是对现有答案的补充答案,这些答案没有解决一个非常常见的问题(尤其是在初学者中):包括在单独的源文件中实现的模板的头文件。


基本上,我有多个 .cpp 文件,其中包含我为使用二叉树、BST、链表等而制作的所有函数。

自从:

  • 您已经用标记了您的问题,并且
  • C++ 有模板,并且
  • 你有类似容器的数据结构(BST、链表等),我假设它们使用模板

我想指出其他答案中未提及的一个常见问题。 考虑以下两个文件:

文件class.hpp

#pragma once // Non-standard but supported by almost (if not) all compilers

// Some class
template<class T> class Class {
    T data;
public:
    Class();
};

// Some function
template<class T> T square(const T& x);

文件class.cpp

#include "class.hpp"
#include <iostream>

template<class T> Class<T>::Class()
{
    std::cout << "Class1()" << '\n';
}

template<class T> T square(const T& x)
{
    return x*x;
}

尝试实例化一个Class对象,或调用square()

int main()
{
    Class<int> clss; // should output Class1()
    std::cout << square<int>(3); // should print 9
}

编译:

g++ class.cpp main.cpp -o cpptest.exe
./gcc/bin/ld.exe: main.cpp(.text+0x15): undefined reference to 'Class<int>::Class()'
./gcc/bin/ld.exe: main.cpp(.text+0x28): undefined reference to 'int square<int>(int const&)'

问题

1. 模板

想象一下你有这个功能:

template<class T> T add(const T& x, const T& y)
{
    return x + y;
}

你这样称呼它:

int res = add<int>(2, 3);

当编译器创建函数实现的副本时(即编译器看到对add()的调用时):

int add(const int& x, const int& y)
{
    return x + y;
}

然后你的电话将变成:

int res = add(2, 3);

这显然意味着模板函数实现必须在找到调用时对编译器可见,因此编译器将能够对其进行复制。 为了让编译器找到/看到它,它必须在同一个翻译单元中。

这就是模板魔法在幕后工作的方式。 有关更多信息,请参阅此答案

2. 编译器和链接器

完成编译步骤:

  1. 将所有#include指令替换为包含的文件内容。
  2. 分别编译每个.cpp文件(也称为“翻译单元”)。 结果将是每个单元的目标文件。 请注意,某些代码(如main.cpp中的)引用了另一个代码( class.cpp )。 这将在下一步中解决。
  3. 将翻译单元完全链接到一个可执行文件中。 (有关更多详细信息,请参阅JMAA答案)。

应用上面的步骤 1,我们将有两个翻译单元( .cpp文件):

文件class.cpp

template<class T> class Class {
    T data;
public:
    Class();
};

template<class T> T square(const T& x);

/* <iostream> code ... */

template<class T> Class<T>::Class()
{
    std::cout << "Class1()" << '\n';
}

template<class T> T square(const T& x)
{
    return x*x;
}

文件main.cpp

template<class T> class Class {
    T data;
public:
    Class();
};

template<class T> T square(const T& x);

/* <iostream> code ... */

int main()
{
    Class<int> clss;
    std::cout << square<int>(3);
}

应用上面的步骤 2,我们将只有地址:

文件class.cpp

/* Nothing to generate for 'Class' definition. Remember, templates are just models (hence the word "template"), not concrete classes/functions. */

/* Nothing to generate for square()'s definition: It's a template, not concrete. */

iostream_object_code

/* Nothing to generate for 'Class' implementation. It's a template, not concrete. */

/* Nothing to generate for square()'s implementation: It's a template, not concrete. */

文件main.cpp

/* Class is not concrete: nothing is generated. */

/* square() is not concrete: nothing is generated. */

iostream_object_code

int main()
{
    class_int_obj_placeholder clss;
    __operator_xx(cout_obj_ref, fn100017(3));
}

应用上面的步骤 3,您将得到undefined reference 错误:链接器不知道任何其他翻译单元中的任何fn100017() 对于clss对象也是如此。

解决方案

  1. 显而易见的解决方法是将定义和实现放在同一个头文件中。 这样,当包含在main.cpp中时,编译器都可以看到它们。
  2. 或者,如果您想将定义和实现分开,请将您的实现放在头文件中,然后将其包含在定义头的末尾:

文件class.hpp

#pragma once

template<class T> class Class {
    T data;
public:
    Class();
};

template<class T> T square(const T& x);

#include "class_impl.h"

文件class_impl.h

#include <iostream>

template<class T> Class<T>::Class()
{
    std::cout << "Class1()" << '\n';
}

template<class T> T square(const T& x)
{
    return x*x;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM