ArrayList.h
#pragma once
template <class T>
class ArrayList
{
public:
ArrayList(int size);
~ArrayList(void);
private:
T* arrayList;
int maxSize;
};
ArrayList.cpp
#include "ArrayList.h"
template <class T>
ArrayList<T>::ArrayList(const int size)
{
maxSize = size;
arrayList = new T[maxSize];
}
template <class T>
ArrayList<T>::~ArrayList(void)
{
delete [] arrayList;
}
源.cpp
#include <iostream>
#include "ArrayList.h"
using namespace std;
int main()
{
ArrayList<int> list(2);
system("pause");
return 0;
}
出现的错误是
将三个文件写到一个文件里后就没有错误了
#include <iostream>
using namespace std;
template <class T>
class ArrayList
{
public:
ArrayList(int size);
~ArrayList(void);
private:
T* arrayList;
int maxSize;
};
template <class T>
ArrayList<T>::ArrayList(int size)
{
maxSize = size;
arrayList = new T[maxSize];
}
template <class T>
ArrayList<T>::~ArrayList(void)
{
delete [] arrayList;
}
int main()
{
ArrayList<int> list(2);
system("pause");
return 0;
}
请问这是为什么
怪我咯2017-04-17 13:06:54
簡單答案是「模板要寫在.h檔裡」。
但我想這不是題主想要的。
要搞清楚這問題,你得先明白分開.h和.cpp的意義。
平常我們分開.cpp和.h,為的並不是模組化。
模組化本身體現在“把不同的東西分別放在不同的文件”,但.cpp和.h卻是“把相同的東西分別放在兩個文件”。
事實上,你大可以完全不寫.h檔,只寫.cpp,然後#include "bla.cpp",一樣可以透過編譯。
(只要.cpp裡的函數/變數/類別等的名字沒撞車…)
分開.h和.cpp的最大原因,在於把各.cpp分開編譯。
在建構程式時,我們把所有.cpp(原始碼檔)編譯成.o(目標檔),然後把.o整合成執行檔(或函式庫)。
假設我們的軟體專案裡,有100個.cpp文件,而在上次建構之後,我們只改了其中一個。
那麼最好的情況是只有一個.o文件需要重新編譯,其餘99個可以直接用來整合可執行文件,省下編譯時間。
這對建構大型軟體來說很有用,總不能你改了Office的一行程式碼,就把數十GB的源碼都重新編譯吧…
然後我們有另一個問題:
.cpp全部都分開了,互相不知道彼此的存在,那如果a.cpp要用到b.cpp裡的東西,怎麼辦?
a.cpp需要知道b.cpp提供了甚麼功能,但不需要知道那些功能是怎麼實現的。
而「展示所提供的功能」就是.h檔案的作用:我提供了名為BlaBlaBla的類,裡面有某某變數某某方法,還有個叫做doSomething的函數,它接受兩個int作為參數,…
於是所有.cpp只需要引入.h,看看能做些什麼甚麼就行了。
它們不需要管.h裡說的那些功能是怎樣實現的,反正只要知道最終整合為可執行文件事,這些事都能完成,就夠了。
這就實現了依賴分離,從而讓各.cpp可以分開編譯。
但模板不同:模板沒辦法分開編譯。
為甚麼?看看以下的範例:
template <typename T>
T* makeArray(size_t n) {
return new T[n];
}
編譯器看到了這行程式碼,它應該做甚麼?
一般情況下,假如編譯器看到一個類似的東西:
SomeClass* makeArray(size_t n) {
return new SomeClass[n];
}
它應該在.o裡寫下類似這樣的二進位代碼:
分配一段足够长的空间(n * sizeof(SomeClass))
找到SomeClass对应的构造函数
对空间里的每一个sizeof(SomeClass)大小的位置,执行一遍SomeClass的构造函数
返回空间的地址
但在模板的例子裡,它辦不到:它根本不知道T是甚鬼,sizeof(T)不明,無法編譯!
另一個問題在於,模板有些特性,讓你不到實際知道T之前,根本不知道編譯出來的程式碼長甚麼樣子。
其中一個叫做偏特化:
template<typename T>
int getValue(T t) {
return t.value;
}
template<>
int getValue(int t) {
return 0;
}
這東西怎麼編譯?在不知道T是甚麼的情況下,你根本不知道該用上面的版本還是下面的版本…
因此,模板程式碼無法被編譯成.o檔。
其他.cpp要使用這段模板,只有直接把它複製貼上到自己的程式碼裡,也就是#include幹的事。
事實上,這也體驗了「模板」這個名字:我只是個原始碼的模板而已,是用來方便你複製貼上然後修改的,不是用來編譯的!
黄舟2017-04-17 13:06:54
我來告訴題主你終極的答案。
其實這個模板寫cpp裡面的事情早就有人propose了。世界上有一個最屌的公司叫EDG,負責寫正確的C++編譯器,然後告訴標準委員會他們的看法,順便賣一下他們的C++/Java/Fortran的編譯器前端。他們花了兩年終於把這件事給做出來了,裡麵包括如何把模板程式碼的AST序列化進obj/lib以便於在連結的時候再展開重新編譯一邊等超級複雜的工作。最後得出一個結論:
這麼屌的功能,只有我們知道怎麼做,你們是做不出來的。
所以全世界就作罷了,再也不提這件事情。
因此其它的答案說了一大堆道理其實都是不正確的,因為並沒有什麼理由阻止C++編譯器在鏈接的時候才編譯模板的源代碼,這全部都是工程上的取捨。做這件事情,好處又幾乎沒有,但是編譯器的複雜度又上升了一大截。所以沒有什麼意義。
伊谢尔伦2017-04-17 13:06:54
如果不用#pragma once
改成用這個試試看呢
#ifndef ARRAY_LIST_H
#define ARRAY_LIST_H
#endif