關於C++編譯鏈接和模板函數


一,關於編譯鏈接
編譯指的的把編譯單元生成目標文件的過程

鏈接是把目標文件鏈接到一起的過程

編譯單元:可以認為是一個.c或者.cpp文件。每個編譯單元經過預處理會得到一個臨時的編譯單元。預處理會間接包含其他文件還會展開宏調用。

每個編譯單元編譯成目標文件后會暴露自己內部的符號。
(比如有個fun函數,就會暴露出於fun函數對應的符號,其他的函數和變量也是一樣的。但是也有不會暴露出去的,比如加了static修飾的函數或變量)
每個目標文件都有自己的符號導入表和符號導出表。


鏈接器根據自己所需要的符號去找其他的目標文件。
(假如main用到了別的文件定義發fun函數,在鏈接的過程中,鏈接器知道mian需要fun符號,然后去其他的目標文件總找。如果找到了就鏈接起來。找不到就報鏈接錯誤)

二、模板函數
模板函數的代碼並不能直接編譯成二進制代碼,其中要有一個實例化的過程。模板被用到的時候才會進行實例化。

1.假設有個test.h里面聲明了模板函數。
test.cpp實現了那個模板函數。
main用到了那個模板函數。
編譯器會編譯test.cpp編譯單元和main.cpp編譯單元。
編譯test.cpp時無法給出A<int>::fun這樣的符號
main.cpp需要一個這樣的符號A<int>::fun。

在分離式編譯的環境下,編譯器編譯某個cpp文件時並不知道另外的cpp的存在,也不會去查找(當遇到未決符號時他會寄希望於鏈接器)。
這種模式在沒有模板的情況下運行良好,但是遇到模板時就不行了,因為模板僅在需要的時候才會實例化出來。
所以當編譯器只看到模板的聲明時,它不能實例化該模板,只能創建一個具有外部連接的符號,並期待鏈接器能夠將符號的地址決議找出來。
然而實現該模板的cpp文件並沒有用到該模板時,編譯器就不會去實例化。
所以整個工程當中找不到模板實例的代碼,鏈接器就找不到那個符號。就會報錯了。

3.實例:
test.h

#ifndef __CAR_H__
#define __CAR_H__
#include<iostream>
using namespace std;
#define IN_CPP 1
template <class T>
class car
{
public:
    car(T a);
    void print();
public:
    T data;
};
#if IN_CPP
#else
template <class T>
car<T>::car(T a)
{
    data = a;
}
template <class T>
void car<T>::print()
{
    cout << "data = " << data << endl;
}
#endif
#endif // __CAR_H__

test.cpp

#include "car.h"
#if IN_CPP
template <class T>
car<T>::car(T a)
{
    data = a;
}
template <class T>
void car<T>::print()
{
    cout << "data = " << data << endl;
}
#endif
void callTest()
{
    car<int> a(33);
    a.print();
}

main.cpp

#include<iostream>
#include "car.h"
using namespace std;
void fun()
{
    cout << "fun() +++" << endl;
    car<int> a(99);
    a.print();
}
int main()
{
    fun();
    return 0;
}

分析:

IN_CPP 如果是0:就相當於聲明實現都在頭文件中。這樣main.cpp是可以編譯運行的。
IN_CPP 如果是1:說明聲明跟實現分開了。這種情況main.cpp鏈接時找不到 car構造相關的函數,也找不到模板類car中print的函數。會報兩個鏈接錯。
但是如果在test.cpp寫個函數(callTest())調用car的構造和print,相當於實例化了那兩個類模板函數。就會導出那兩個函數的符號。假如只調用一個構造,那么print就沒有實例化。main也會鏈接失敗
然后在main.cpp就可以調用到了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM