![](/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.