![](/img/trans.png)
[英]how can I use a function from one cpp file in another cpp file?
[英]How can I call functions from one .cpp file in another .cpp file?
在标头中声明函数
//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 ...
您似乎在构建程序方面缺少一些非常基本的概念。 我将为您提供一个非常基本的入门,但是您将不得不在其他地方找到更完整的答案,才能真正了解正在发生的事情并获得设置的详细信息。
通常,您告诉编译器编译每个cpp文件。 当cpp文件具有#include
语句时,该语句基本上将include
d文件复制并粘贴到cpp文件中,然后再进行编译(这由预处理程序完成)。 编译器处理的每个完整单元(包含附件的cpp文件)都称为转换单元 。 每个翻译单元产生一个目标文件 。
目标文件包含已编译的代码,但是它们通常不完整。 也就是说,它们包含对其中未包含的代码的引用。 基本上,他们可以说“现在调用此函数,我不知道它在哪里或在做什么,但是您应该调用它”。
然后使用链接器将目标文件(可能与库一起)链接到可执行文件(或库)中。 链接器的工作是通过在其他目标文件和库中查找相关代码来“解析”对每个目标文件中外部代码的所有引用。
库有两种类型: 共享库(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、链表等而制作的所有函数。
自从:
我想指出其他答案中未提及的一个常见问题。 考虑以下两个文件:
文件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&)'
想象一下你有这个功能:
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);
这显然意味着模板函数实现必须在找到调用时对编译器可见,因此编译器将能够对其进行复制。 为了让编译器找到/看到它,它必须在同一个翻译单元中。
这就是模板魔法在幕后工作的方式。 有关更多信息,请参阅此答案。
完成编译步骤:
#include
指令替换为包含的文件内容。.cpp
文件(也称为“翻译单元”)。 结果将是每个单元的目标文件。 请注意,某些代码(如main.cpp
中的)引用了另一个代码( class.cpp
)。 这将在下一步中解决。 应用上面的步骤 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
对象也是如此。
main.cpp
中时,编译器都可以看到它们。 文件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.