天天看點

【C++ STL學習筆記】C++ STL基礎C++ STL是什麼,有什麼用?C++ STL的發展曆程是怎樣的?C++ STL版本有哪些?熟練使用STL标準庫是每個C++程式員的必備技能!泛型是什麼,C++泛型程式設計又是什麼?C++ STL基本組成(6大元件+13個頭檔案)如何衡量一個算法的執行效率?GNU開源精神及其發展曆程

文章目錄

  • C++ STL是什麼,有什麼用?
    • 學STL能幹什麼?
  • C++ STL的發展曆程是怎樣的?
  • C++ STL版本有哪些?
    • HP STL
    • SGI STL
    • STLport
    • PJ STL
    • Rouge Wave STL
  • 熟練使用STL标準庫是每個C++程式員的必備技能!
  • 泛型是什麼,C++泛型程式設計又是什麼?
  • C++ STL基本組成(6大元件+13個頭檔案)
  • 如何衡量一個算法的執行效率?
  • GNU開源精神及其發展曆程

C++ STL是什麼,有什麼用?

在已有 C++ 尤其是 C++ 模闆的基礎上,從本節開始,我們開始系統地學習 STL 标準模闆庫,首先來了解什麼是 STL,以及學習 STL 有什麼用?

STL,英文全稱 standard template library,中文可譯為标準模闆庫或者泛型庫,其包含有大量的模闆類和模闆函數,是 C++ 提供的一個基礎模闆的集合,用于完成諸如輸入/輸出、數學計算等功能。

STL 最初由惠普實驗室開發,于 1998 年被定為國際标準,正式成為 C++ 程式庫的重要組成部分。值得一提的是,如今 STL 已完全被内置到支援 C++ 的編譯器中,無需額外安裝,這可能也是 STL 被廣泛使用的原因之一。

STL 就位于各個 C++ 的頭檔案中,即它并非以二進制代碼的形式提供,而是以源代碼的形式提供。

從根本上說,STL 是一些容器、算法和其他一些元件的集合,所有容器和算法都是總結了幾十年來算法和資料結構的研究成果,彙集了許多計算機專家學者經驗的基礎上實作的,是以可以說,STL 基本上達到了各種存儲方法和相關算法的高度優化。

注意,這裡提到的容器,本質上就是封裝有資料結構的模闆類,例如 list、vector、set、map 等,有關這些容器的具體用法,後續章節會做詳細介紹。

學STL能幹什麼?

為了讓讀者清楚地了解 STL 是什麼,使用 STL 程式設計有哪些優勢,這裡舉一個使用 STL 的例子。

以 C++ 定義數組的操作為例,在 C++ 中如果定義一個數組,可以采用如下方式:

int a[n];

這種定義數組的方法需要事先确定好數組的長度,即 n 必須為常量,這意味着,如果在實際應用中無法确定數組長度,則一般會将數組長度設為可能的最大值,但這極有可能導緻存儲空間的浪費。

是以除此之外,還可以采用在堆空間中動态申請記憶體的方法,此時長度可以是變量:

int *p = new int[n];

這種定義方式可根據變量 n 動态申請記憶體,不會出現存儲空間浪費的問題。但是,如果程式執行過程中出現空間不足的情況時,則需要加大存儲空間,此時需要進行如下操作:

  1. 新申請一個較大的記憶體空間,即執行

    int * temp = new int[m];

  2. 将原記憶體空間的資料全部複制到新申請的記憶體空間中,即執行

    memecpy(temp, p, sizeof(int)*n);

  3. 将原來的堆空間釋放,即執行

    delete [] p; p = temp;

而完成相同的操作,如果采用 STL 标準庫,則會簡單很多,因為大多數操作細節将不需要程式員關心。下面是使用向量模闆類 vector 實作以上功能的示例:

vector <int> a; //定義 a 數組,目前數組長度為 0,但和普通數組不同的是,此數組 a 可以根據存儲資料的數量自動變長。
//向數組 a 中添加 10 個元素
for (int i = 0; i < 10 ; i++)
    a.push_back(i)
//還可以手動調整數組 a 的大小
a.resize(100);
a[90] = 100;
//還可以直接删除數組 a 中所有的元素,此時 a 的長度變為 0
a.clear();
//重新調整 a 的大小為 20,并存儲 20 個 -1 元素。
a.resize(20, -1)
           
注意,初學者隻需結合注釋,大概了解代碼功能即可,有關代碼中涉及到具體知識,後續會做詳細介紹。

對比以上兩種使用數組的方式不難看出,使用 STL 可以更加友善靈活地處理資料。是以,大家隻需要系統地學習 STL,便可以集中精力去實作程式的功能,而無需再糾結某些細節如何用代碼實作。

C++ STL的發展曆程是怎樣的?

Alexander Stepanov(後被譽為 STL 标準模闆庫之父,後簡稱 Stepanov),1950 年出生與前蘇聯的莫斯科,他曾在莫斯科大學研究數學,此後一直緻力于計算機語言和泛型庫研究。

在 20 世紀 70 年代,Stepanov 開始考慮,在保證效率的前提下,是否能将算法從諸多具體應用之中抽象出來?為了驗證自己的思想,他和紐約州立大學教授 Deepak Kapur 以及倫塞裡爾技術學院教授 David Musser 共同開發了一種叫做 Tecton 的語言,盡管這次嘗試沒有取得實用性的成果,但卻給了 Stepanov 很大的啟示。

在随後的幾年中,他又和 David Musser 等人先後用 Schema 語言(一種 Lisp 語言的變種)和 Ada 語言建立了一些大型程式庫。Stepanov 逐漸意識到,在當時的面向對象程式設計思想中存在一些問題,比如抽象資料類型概念所存在的缺陷,他希望通過對軟體領域中各組成部分的分類,逐漸形成一種軟體設計的概念性架構。

1987 年,在貝爾實驗室工作的 Stepanov 開始首次采用 C++ 語言進行泛型軟體庫的研究。由于當時的 C++ 語言還沒有引入模闆的程式設計技術,泛型庫隻能是通過 C++ 的繼承機制來開發,代碼表達起來非常笨拙。

但盡管如此,Stepanov 還是開發出了一個龐大的算法庫。與此同時,在與 Andrew Koenig(前 ISO C++ 标準化委員會主席)和 Bjarne Stroustrup(C++ 語言的創始人)等頂級大師們的共事過程中,Stepanov 開始注意到 C/C++ 語言在實作其泛型思想方面所具有的潛在優勢。

就拿 C/C++ 中的指針而言,它的靈活與高效運用使後來的 STL 在實作泛型化的同時更是保持了高效率。另外,在 STL 中占據極其重要地位的疊代器概念便是源自于 C/C++ 中原生指針的一般化推廣。

1988 年,Stepanov 開始進入惠普的 Palo Alto 實驗室工作,在随後的 4 年中,他從事的是有關磁盤驅動器方面的工作。直到 1992 年,由于參加并主持了實驗室主任 Bill Worley 所建立的一個有關算法的研究項目,才使他重新回到了泛型化算法的研究工作上來。

項目自建立之後,參與者從最初的 8 人逐漸減少,最後隻剩下 Stepanov 和 Meng Lee 兩個人。經過長時間的努力,最終完成了一個包含有大量資料結構和算法部件的龐大運作庫(HP 版本的 C++ STL),這便是現在 STL 的雛形。

1993 年,當時在貝爾實驗室的 Andrew Koenig 看到了 Stepanov 的研究成果,在他的鼓勵與幫助下,Stepanov 于 1993 年 9 月在聖何塞為 ANSI/ISO C++ 标準委員會做了一個題為“The Science of C++ Programming” 的演講,向委員們講述了其觀念。然後又于 1994 年 3 月,在聖疊戈會議上向委員會送出了一份建議書,以期将 STL 通用庫納入 C++ 标準。

盡管這一建議十分龐大,以至于降低了被通過的可能性,但其所包含的新思想吸引了許多人的注意力。随後在衆人的幫助之下,包括 Bjame Stroustrup 在内,Stepanov 又對 STL 進行了改進,同時加入了一個封裝記憶體模式資訊的抽象子產品,也就是現在 STL 中的 allocator(記憶體配置設定器),它使 STL 的大部分實作都可以獨立于具體的記憶體模式,進而獨立于具體平台。

最終在 1994 年的滑鐵盧會議上,委員們通過了提案,決定将 STL 正式納入 C++ 标準化程序之中,随後 STL 便被放進了會議的工作檔案中。自此,STL 終于成為 C++ 家族中的重要一員。

此後,随者 C++ 标準的不斷改進,STL 也在不斷地做着相應的演化。直至 1998 年,ANSI/ISO C++ 标準正式定案,STL 始終是 C++ 标準庫不可或缺的重要組成部分。

C++ STL版本有哪些?

自 1998 年 ANSI/ISO C++ 标準正式定案,C++ STL 規範版本正式通過以後,由于其實開源的,各個 C++ 編譯器廠商在此标準的基礎上,實作了滿足自己需求的 C++ STL 泛型庫,主要包括 HP STL、SGI STL、STLport、PJ STL、Rouge Wave STL 等。

HP STL

HP STL 是 Alexandar Stepanov(STL 标準模闆庫之父,文章後續簡稱 Stepanov)在惠普 Palo Alto 實驗室工作時,與 Meng Lee 合作完成的。HP STL 是開放源碼的,即任何人都可以免費使用、複制、修改、釋出和銷售該軟體以及相關文檔,但前提是必須在相關文檔中,加入 HP STL 版本資訊和授權資訊。

HP STL 是 C++ STL 的第一個實作版本,其它版本的 C++ STL 一般是以 HP STL 為藍本實作出來的。不過,現在已經很少直接使用此版本的 STL 了。

SGI STL

Stepanov 在離開 HP 之後,就加入到了 SGI 公司,并和 Matt Austern 等人開發了 SGI STL。嚴格意義上來說,它是 HP STL 的一個繼承版本。和 HP STL 一樣,SGI STL 也是開源的,其源代碼的可讀性可非常好,并且任何人都可以修改和銷售它。

注意,和 STL 官方版本來說,SGI STL 隻能算是一個“民間”版本,是以并不是所有支援 C++ 的編譯器都支援使用 SGI STL 模闆庫,唯一能确定的是,GCC(Linux 下的 C++ 編譯器)是支援的,是以 SGI STL 在 Linux 平台上的性能非常出色。

STLport

為了使 SGI STL 的基本代碼都适用于 VC++ 和 C++ Builder 等多種編譯器,俄國人 Boris Fomitchev 建立了一個 free 項目來開發 STLport,此版本 STL 是開放源碼的。

PJ STL

PJ STL(全稱為 P.J. Plauger STL)是由 P.J.Plauger(美國人,1965 年畢業于普林斯頓大學,實體專業學士)參照 HP STL 實作出來的,也是 HP STL 的一個繼承版本,是以該頭檔案中不僅含有 HP STL 的相關授權資訊,同時還有 P.J.Plauger 本人的版權資訊。

其實 PJ STL 是 P.J.Plauger 公司的産品,盡管該公司當時隻有 3 個人。

PJ STL 被 Visual C++ 編譯器所采用,但和 PH STL、SGI STL 不同的是,PJ STL 并不是開源。

Rouge Wave STL

該版本的 STL 是由 Rouge Wave 公司開發的,也是繼承 HP STL 的一個版本,它也不是開源的。

Rouge Wave STL 用于 Borland C++ Builder 編譯器中,我們可以在 C++ Builder 的 Inculde 子目錄中找到該 STL 的所有頭檔案。

值得一提的是,盡管 Rouge Wave STL 的性能不是很好,但 C++ Builder 對 C++ 語言标準的支援還算不錯,是以在一定程度上使 Rouge Wave STL 的表現得以改善。

遺憾的是,由于 Rouge Wave STL 長期沒有更新且不完全符合标準,是以 Rouge Wave STL 在 6.0 版本時改用了 STLport 版本(之後的版本也都采用了 STLport),不過考慮到和之前版本的相容,6.0 版本中依舊保留了 Rouge Wave STL。

Rouge Wave 公司在 C++ 程式庫領域應該說是鼎鼎大名,對 C++ 标準化的過程出力甚多。不過 Rouge Wave STL 版本不僅更新頻率慢,費用還高,基于這兩個原因,Borland 在 6.0 版本決定棄用 Rouge Wave STL 而改用 STLport。

熟練使用STL标準庫是每個C++程式員的必備技能!

C++ 标準程式庫發展至今,幾乎所有内容都被設計為了模闆的形式,STL 已經成為 C++ 程式庫的重要組成部分。可以這麼說,如果 C++ 不支援 STL 标準模闆庫,就無法使用程式庫。那麼,C++ 為什麼要引入 STL 呢?

在大多數人看來,計算機既神秘有能幹,但在程式員的眼中,計算機又蠢又笨,唯一的優點就是運算速度比人快,不給指令什麼都幹不了,就是給指令,計算機也不能靈活運用。

比如說,在 C++ 中,同樣一個加法,不同的資料類型,要給出不同的運作代碼:

#include<iostream>
using namespace std;
//處理整形之間的加法
int addInt(int m, int n) {
    return m + n;
}
//處理浮點類型值之間的加法
double addDouble(double i, double j) {
    return i + j;
}
//......
int main()
{
    cout << addInt(1, 2) << endl << addDouble(1.2, 2.1);
    return 0;
}
           

運作結果為:

3

3.3

像這樣,對于每一種資料類型,我們都必須給計算機設計一個單獨的函數,實在太繁瑣了。由此可以感受到,計算機并不具備人類的基本思維,處理問題不靈活。

為了讓計算機不斷接近人類的認知能力,科學家們想了很多辦法,比如使用面向對象開發技術,通過類的封裝和函數重載,可以部分解決上面的問題:

#include<iostream>
using namespace std;
class calc {
public:
    //處理整形之間的加法
    int add(int m, int n) {
        return m + n;
    }
    //處理浮點類型值之間的加法
    double add(double i, double j) {
        return i + j;
    }
    //......
};
int main()
{
    calc a;
    cout << a.add(1, 2) << endl << a.add(1.2, 2.1);
    return 0;
}
           

運作結果為:

3

3.3

有關 C++ 函數重載,可閱讀《C++函數重載詳解》一節。

建立這樣的類之後,當通過類對象調用 add 方法時,就無需考慮參數的具體資料類型了。但從某種程式上來說,這也僅是讓計算機聰明了一點點。

為了讓程式更加智能、人性化,經過科學家們持續的努力,C++ 引入了模闆這個功能。模闆可以認為是針對一個或多個尚未明确的類型而編寫的一個個函數,是 C++ 的一個新特性。

通過引入模闆,C++ 引申出了泛型程式設計技術。簡單的了解泛型程式設計,即使用該技術編寫的代碼,可以支援多種資料類型。也就是說,通過泛型程式設計,能編寫出可重複利用的程式代碼,并且其運作效率和針對某特定資料類型而設計的代碼相同。由此可見,C++ 很需要泛型這種新的程式設計模式,可以減輕程式設計的工作量,增強代碼的重用性。

有關泛型及泛型程式設計,讀者可閱讀《泛型是什麼,C++泛型程式設計又是什麼?》一文做詳細了解。

在 C++ 支援模闆功能,引入了泛型程式設計思想的基礎上,C++ 程式員們想編寫出很多通用的針對不同資料類型的算法,其中 STL 脫穎而出成為 C++ 标準,并被引入 C++ 标準程式庫。

STL 是一個具有高度可用性、高效的模闆庫,該庫包含了諸多在計算機科學領域中常用的基礎資料結構和算法,掌握了 STL 标準,很多功能就無需自己費心費力的去實作了(不用重複的造輪子),直接拿來用即可。

總的來說,STL 模闆庫是 C++ 标準程式庫的重要組成部分,為 C++ 程式員提供了大量的可擴充的程式架構,高度實作了代碼的可重用性,并且它是内置的,不需要額外安裝,使用非常友善。

泛型是什麼,C++泛型程式設計又是什麼?

在計算機程式設計領域,為了避免因資料類型的不同,而被迫重複編寫大量相同業務邏輯的代碼,人們發展的泛型及泛型程式設計技術。

那麼,什麼是泛型呢?本節就帶領讀者深度剖析一下這個問題。

是以泛型,實質上就是不使用具體資料類型(例如 int、double、float 等),而是使用一種通用類型來進行程式設計的方法,該方法可以大規模的減少程式代碼的編寫量,讓程式員可以集中精力用于業務邏輯的實作。

為了更好地說明使用具體資料類型有多麼麻煩,這裡先舉個例子,假設客戶需要一個函數,功能是傳回兩個 int 類型資料中較大的那個,很多讀者自然而然會編寫如下代碼:

int maxt(int x, int y) {
    return (x > y) ? x : y;
}
           

可是沒過幾天,該使用者又提出需要編寫一個傳回兩個 double 類型資料中較大的那個,于是我們需要之前的代碼進行修改:

double maxt(double x, double y) {
    return (x > y) ? x : y;
}
           

之後,該使用者又提出需要再編寫一個能傳回兩個 char 類型資料中較大的那個…。可以看到,隻是因為資料類型不同,就迫使我們不得不把具有相同功能的代碼寫了若幹遍,這樣的實作方法簡直令人崩潰。

為了解決類似的問題,有聰明的人将代碼修改成如下的樣子:

T maxt(T x, T y) {
    return (x > y) ? x : y;
}
           

如此,當使用者需要某個資料類型的 maxt 函數時,我們隻需要把其中的 T 替換成使用者需要的實際資料類型就行了。

那麼,代碼中的 T 是什麼呢?很明顯,這是一個占位符,更确切的說是一個類型占位符。也就是說,将來在 T 這個位置上的是一個真實、具體的資料類型,至于到底是哪個類型,完全取決于使用者的需求。

當然,如果硬要給 T 這種類型占位符也叫做一種資料類型,提供這種想法的發明者稱它為泛型(generic type),而使用這種類型占位符的程式設計方式就被稱為泛型程式設計。

值得一提的是,既然泛型并不是真實的資料類型,那麼使用泛型編寫的代碼也就不是真正的程式實體,隻能算是一個程式實體的樣闆。故此,通常形象的将這種使用了泛型的代碼稱為模闆,由模闆生成實際代碼的過程稱為模闆的具體實作。

注意,類型占位符的替換工作,不需要人為操控,可以完全交由計算機來完成,更準确的說,是交由編譯器在編譯階段來完成模闆的具體實作。

總之一句話,泛型也是一種資料類型,隻不過它是一種用來代替所有類型的“通用類型”。在 C++ 中,用以支援泛型應用的就是标準模闆庫 STL,它提供了 C++ 泛型設計常用的類模闆和函數模闆。

有關 STL 模闆庫,由于不是本節重點,這裡不再贅述,後續章節會做詳細介紹。

C++ STL基本組成(6大元件+13個頭檔案)

通常認為,STL 是由容器、算法、疊代器、函數對象、擴充卡、記憶體配置設定器這 6 部分構成,其中後面 4 部分是為前 2 部分服務的,它們各自的含義如表 1 所示。

STL的組成 含義
容器 一些封裝資料結構的模闆類,例如 vector 向量容器、list 清單容器等。
算法 STL 提供了非常多(大約 100 個)的資料結構算法,它們都被設計成一個個的模闆函數,這些算法在 std 命名空間中定義,其中大部分算法都包含在頭檔案 中,少部分位于頭檔案 中。
疊代器 在 C++ STL 中,對容器中資料的讀和寫,是通過疊代器完成的,扮演着容器和算法之間的膠合劑。
函數對象 如果一個類将 () 運算符重載為成員函數,這個類就稱為函數對象類,這個類的對象就是函數對象(又稱仿函數)。
擴充卡 可以使一個類的接口(模闆的參數)适配成使用者指定的形式,進而讓原本不能在一起工作的兩個類工作在一起。值得一提的是,容器、疊代器和函數都有擴充卡。
記憶體配置設定器 為容器類模闆提供自定義的記憶體申請和釋放功能,由于往往隻有進階使用者才有改變記憶體配置設定政策的需求,是以記憶體配置設定器對于一般使用者來說,并不常用。
關于表 1 中羅列的 STL 的構成,初學者簡單了解即可,後續章節将專門對它們做系統的深入講解。

另外,在惠普實驗室最初發行的版本中,STL 被組織成 48 個頭檔案;但在 C++ 标準中,它們被重新組織為 13 個頭檔案,如表 2 所示。

關于這些頭檔案的作用和用法,本節不做過多贅述,後續章節會做詳細介紹。

按照 C++ 标準庫的規定,所有标準頭檔案都不再有擴充名。以 為例,此為無擴充名的形式,而 <vector.h> 為有擴充名的形式。

但是,或許是為了向下相容,或許是為了内部組織規劃,某些 STL 版本同時存儲具備擴充名和無擴充名的兩份檔案(例如 Visual C++ 支援的 Dinkumware 版本同時具備 <vector.h> 和 );甚至有些 STL 版本同時擁有 3 種形式的頭檔案(例如 SGI 版本同時擁有 、<vector.h> 和 <stl_vector.h>);但也有個别的 STL 版本隻存在包含擴充名的頭檔案(例如 C++ Builder 的 RaugeWare 版本隻有 <vector.h>)。

建議讀者養成良好的習慣,遵照 C++ 規範,使用無擴充名的頭檔案。

如何衡量一個算法的執行效率?

學習 C++ 标準庫,特别是 STL,經常需要考量算法和成員函數的效能(也就是運作效率,又稱複雜度),是以每個學習 STL 的讀者都需要掌握一種衡量算法(或成員函數)複雜度的方法,目前最常用的方法稱為大 O 表示法(注意,不是數字 0,而是字母 O)。

使用大 O 表示法衡量某個算法的複雜度,其實就是将該算法的運作時間用輸入量為 n 的函數表示出來。這裡的輸入量 n 在 STL 中通常指的是算法操作的元素個數。

舉個例子,當算法運作時間随元素個數成線性增長時(即如果元素個數呈倍數增長,運作時間也呈倍數增長),該算法的複雜度用 O(n) 來表示;反之,如果算法的運作時間和輸入量 n 無關,則該算法的複雜度就用 O(1) 來表示。

表 1 列出了常見的算法複雜度種類,以及使用大 O 表示法表示的複雜度。

算法複雜度種類 含義 大 O 表示法
常數階 算法運作時間和所操作的元素個數無關 O(1)
對數階 算法運作時間随所操作的元素個數呈對數增長 O(log(n))
線性階 算法運作時間随所操作的元素個數呈線性增長 O(n)
指數階(m次方,m為數字) 算法運作時間随所操作的元素個數呈 m 次方增長 O(nm) 常見的有 O(n2)、O(n3) 等

值得注意的是,大 O 表示法并不關心算法運作所消耗的具體時間,換句話說,對于那些影響算法運作效率較小的因素,使用大 O 表示法表示時會直接将其忽略。例如,某個算法運作的複雜度為 O(n),呈線性增長,但至于線性增長的具體程度(是 100n 還是 2n),在大 O 表示法看來,它們是一樣的。也就是說,采用這種測量法則,任何兩個線性算法都将被視為具有相同的複雜度。

采用大 O 表示法甚至會出現這種一種情況,即帶有巨大常量的線性算法,很有可能會比小常量的指數算法更受歡迎,因為該方法無法顯示出真實的運作時間。

是以請讀者記住,大 O 表示法隻是某種度量方法,它所顯示的算法的最佳複雜度,并不一定就是真正的最佳(最快)算法。

複雜度 元素個數
種類 大 O 表示 1 2 5 10 50 100 1 000 10 000
常量階 O(1) 1 1 1 1 1 1 1 1
對數階 O(log(n)) 1 2 3 4 6 7 10 13
線性階 O(n) 1 2 5 10 50 100 1 000 10 000
2次方 O(n2) 1 4 25 100 2500 10 000 1 000 000 100 000 000

表 2 列出了常用的複雜度随元素個數增長的變化程式。可以看到,當算法處理的元素較少時,各複雜度的差别很小,此時算法效率的優劣往往受被大 O 表示法忽略部分的影響更大。而當處理元素個數越多,各複雜度的差别越大,此時複雜度被忽略的部分就變得無關緊要了。

是以,在考量算法的複雜度時,輸入量 n (操作元素的個數)的值必須足夠大才有意義。

GNU開源精神及其發展曆程

通過前面對 STL 各種“民間”版本的介紹我們知道,全世界所有的 STL 實作版本都源于 Stepanov 和 Meng Lee 完成的原始版本(也就是 HP STL),該版本的每一個頭檔案中都有一份聲明,即允許任何人任意運用、拷貝、修改、傳播、販賣這些代碼,無需付費,但唯一的條件是必須将該份聲明置于使用者新開發的檔案内。

如上所說,這種開放源代碼的精神,一般統稱為開源精神。本教程既然使用這些免費開放的源代碼,自然有義務傳承這種開源精神,是以本節将對開源精神及其曆史發展做一下簡單的介紹。

開放源代碼的觀念源自美國人 Richard Stallman(理察·史托曼,如圖 1 所示),他認為私藏源代碼是一種違反人性的罪惡行為,而如果能與他人分享源代碼,便可以讓其他人從中學習,并回饋給原始創作者。封鎖源代碼雖然可以程度不一地保障“智慧可能衍生的财富”,卻阻礙了使用者從中學習和修正錯誤的機會。

【C++ STL學習筆記】C++ STL基礎C++ STL是什麼,有什麼用?C++ STL的發展曆程是怎樣的?C++ STL版本有哪些?熟練使用STL标準庫是每個C++程式員的必備技能!泛型是什麼,C++泛型程式設計又是什麼?C++ STL基本組成(6大元件+13個頭檔案)如何衡量一個算法的執行效率?GNU開源精神及其發展曆程

圖 1 Richard Stallman

1984 年,Stallman 離開麻省理工學院,創立自由軟體基金會(簡稱 FSF),并寫下著名的 GNU 宣言,開始進行名為 GNU 的開放改革計劃。GNU 這個名稱所要表達的含義可以了解為“GNU is Not Unix”,還可以譯為“革奴”,意思是從此革去被奴役的命運。

要知道,在那個時期 Unix 是計算機界的主流作業系統,AT&T(Unix 的創造者)将它分享給許多研究人員,而當所有的研究和分享使 Unix 越來越好時,AT&T 開始思考追加投資并從中獲利,他一方面要求相關研究人員不得公開或透露 Unix 源代碼,一方面贊助 Berkeley(伯克利)大學繼續強化 Unix,導緻後續發展出 BSD 版本,以及更後來的 FreeBSD、OpenBSD、NetBSD 等等。

Stallman 将 AT&T 這種行為視作思想鉗制,于是他開始了稱之為 GNU 的反奴役計劃。GNU 計劃中,最著名的軟體包括 Emacs(Stallman 開發的一個非常靈活的文本編輯器,允許使用者自行增加新功能)、GCC(C/C++ 編譯器)以及後來的 Linux 作業系統。

GNU 以所謂的 GPL(廣泛開放授權)來保護加入該計劃的成員,即使用者可以自由閱讀和修改 GPL 軟體的源代碼,但如果使用者要傳播借助 GPL 軟體而完成的軟體,他們必須也同意 GPL 規範。GPL 對于版權觀念帶來巨大的挑戰,甚至一度被稱為“反版權”。GPL 帶給使用者強大的道德束縛感,“粘”性極強,導緻種種不同的反對意見,包括可能造成經濟競争力薄弱等等。

于是,後來就衍生出各種不同的授權,包括 Library GPL、BSD License 等,這些授權的共同原則就是“開放源代碼”,但由于各種授權的使用者群衆所摻雜的本位主義,再加上精英分子難以妥協的個性,使“開放源代碼”陣營中的各個分支發生意見分歧甚至互相對立,其中屬 GNU CPL 和 BSD License 的擁護者表現最突出。

1998 年,自由軟體社群企圖創造出“開源”這個新名詞來整合各方,他們組成了一個非财團法人的組織,注冊了一個标記,并設立網站。開源的定義共有 9 條,即任何軟體隻要符合這 9 條,就可稱呼自己為開源軟體。

有關開源的 9 條定義,有興趣的讀者可自行百度搜尋了解,這裡不做過多贅述。

繼續閱讀