??所屬專欄:C++??
??作者主頁:嶔某??
C++發展歷史
????????C++的起源可以追溯到1979年,當時BjarneStroustrup(本賈尼·斯特勞斯特盧普,這個翻譯的名字不同的地?可能有差異)在?爾實驗室從事計算機科學和軟件?程的研究?作。?對項?中復雜的軟件開發任務,特別是模擬和操作系統的開發?作,他感受到了現有語?(如C語?)在表達能?、可維護性和可擴展性??的不?。
????????1983年,BjarneStroustrup在C語?的基礎上添加了?向對象編程的特性,設計出了C++語?的雛形,此時的C++已經有了類、封裝、繼承等核?概念,為后來的?向對象編程奠定了基礎。這?年該語?被正式命名為C++。
????????在隨后的?年中,C++在學術界和?業界的應?逐漸增多。?些?學和研究所開始將C++作為教學和研究的?選語?,??些公司也開始在產品開發中嘗試使?C++。這?時期,C++的標準庫和模板等特性也得到了進?步的完善和發展。
????????C++的標準化?作于1989年開始,并成?了?個ANSI和ISO(International Standards Organization)國際標準化組織的聯合標準化委員會。1994年標準化委員會提出了第?個標準化草
案。在該草案中,委員會在保持斯特勞斯特盧普最初定義的所有特征的同時,還增加了部分新特征。
????????在完成C++標準化的第?個草案后不久,STL(Standard Template Library)是惠普實驗室開發的?系列軟件的統稱。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普實驗室?作時所開發出來的。在通過了標準化第?個草案之后,聯合標準化委員會投票并通過了將STL包含到C++標準中的提議。STL對C++的擴展超出C++的最初定義范圍。雖然在標準中增加STL是個很重要的決定,但也因此延緩了C++標準化的進程。
????????1997年11?14?,聯合標準化委員會通過了該標準的最終草案。1998年,C++的ANSI/IS0標準被投?使?。
C++祖師爺
1、起源與初期階段(1980s)
?????????C++的故事開始于上世紀80年代初,比爾·古德斯特(Bjarne Stroustrup)在貝爾實驗室工作,他開始著手創建一種新的編程語言。他的目標是建立一種語言,能夠充分利用C語言的優勢,同時克服C語言的一些限制。在這個過程中,C++作為C的擴展逐漸形成,首個正式版本于1985年發布。
2、標準化與C++98(1990s)
?????????C++在初期經歷了多個版本的迭代和改進,但真正的轉折點發生在1998年,C++的第一個國際標準(ISO/IEC 14882:1998)正式發布。這一標準定義了許多現代C++的特性,包括模板、命名空間、異常處理等,使得C++成為一門更強大、更靈活的編程語言。C++98的發布標志著C++開始進入廣泛接受的階段。
3、技術進步與C++11(2011年)
?????????2011年,C++標準委員會發布了C++11標準,這是C++的一次巨大飛躍,引入了許多重要的新特性,如智能指針、lambda表達式、并發編程支持等。這一版本的發布對C++社區產生了深遠的影響,使得C++在現代軟件開發中更具競爭力。
4、C++14和C++17(2014年和2017年)
?????????C++14在2014年發布,它對C++11進行了一些小的改進和修復。
????????而在2017年,C++標準委員會發布了C++17標準,引入了一系列新特性,如變量模板、文件系統庫、并行算法等,進一步提升了C++的功能和性能。
5、C++20和C++23(2020年和2023年)
????????C++20是C++標準的最新版本,于2020年發布。它引入了許多新特性,包括概念、協程、范圍基于的for循環等,進一步提高了C++的表達力和可讀性。未來,C++標準委員會將繼續推動C++的發展,以適應不斷變化的軟件開發需求。
????????C++23是一個小版本更新,進一步完善和改進現有特性,增加了if consteval、數、falt_map,import std導入標準庫等
6、C++26(制定中……)
命名空間
為什么要有命名空間
????????在C/C++中,變量、函數和后?要學到的類都是?量存在的,這些變量、函數和類的名稱將都存在于全局作?域中,可能會導致很多沖突。使?命名空間的?的是對標識符的名稱進?本地化,以避免命名沖突或名字污染,namespace關鍵字的出現就是針對這種問題的。
????????c語?項?類似下?程序這樣的命名沖突是普遍存在的問題,C++引?namespace就是為了更好的解決這樣的問題
#include<stdio.h>
#include<stdlib.h>int rand = 10;int main()
{printf("%d\n",rand);// 編譯報錯:error C2365: “rand”: 重定義;以前的定義是“函數”return 0;
}
?namespace的定義
????????定義命名空間,需要使?到namespace關鍵字,后?跟命名空間的名字,然后接?對{}即可,{}中即為命名空間的成員。命名空間中可以定義變量/函數/類型等。
????????namespace本質是定義出?個域,這個域跟全局域各?獨?,不同的域可以定義同名變量,所以下?的rand不再沖突了。
????????C++中域有函數局部域,全局域,命名空間域,類域;域影響的是編譯時語法查找?個變量/函數/類型出處(聲明或定義)的邏輯,所以有了域隔離,名字沖突就解決了。局部域和全局域除了會影響編譯查找邏輯,還會影響變量的生命周期,命名空間域和類域不影響變量生命周期。
namespace只能定義在全局,當然他還可以嵌套定義。
項??程中多?件中定義的同名namespace會認為是?個namespace,不會沖突。
C++標準庫都放在?個叫std(standard)的命名空間中。
常規命名空間的定義?
#include<stdio.h>
#include<stdlib.h>
namespace Qinmou
{int rand = 0;int Add(int a, int b){return a + b;}struct Node {int a;struct Node* next;};
}int main()
{// 這?默認是訪問的是全局的rand函數指針printf("%p\n", rand);// 這?指定Qinmou命名空間中的randprintf("%d\n", Qinmou::rand);return 0;
}
嵌套命名空間
#include<stdio.h>
#include<stdlib.h>
namespace Qinmou
{namespace Qin {int a = 0;int Add(int a, int b){return a + b;}}namespace Wang {int a = 1;int Add(int a, int b){return (a + b) * 10;}}
}
int main()
{printf("%d\n", Qinmou::Qin::a);printf("%d\n", Qinmou::Wang::a);printf("%d\n", Qinmou::Qin::Add(1, 2));printf("%d\n", Qinmou::Wang::Add(1, 2));return 0;
}
另外多文件中可以定義同名namespace,編譯器會將他們默認合并到一起,等同于一個namespace。
命名空間使?
編譯查找?個變量的聲明/定義時,默認只會在局部或者全局查找,不會到命名空間??去查找。所以下?程序會編譯報錯。所以我們要使?命名空間中定義的變量/函數,有三種?式:
? 指定命名空間訪問,項?中推薦這種?式。
? using將命名空間中某個成員展開,項?中經常訪問的不存在沖突的成員推薦這種?式。
? 展開命名空間中全部成員,項?不推薦,沖突?險很?,?常?練習程序為了?便推薦使?。
#include<stdio.h>
namespace Qin
{int a = 0;int b = 1;
}
int main()
{// 編譯報錯:error C2065: “a”: 未聲明的標識符printf("%d\n", a);return 0;
}
指定命名空間訪問
int main()
{printf("%d\n", Qin::a);return 0;
}
using將命名空間中的某個成員展開
using Qin::b;
int main()
{printf("%d\n", Qin::a);printf("%d\n", b);return 0;
}
?展開命名空間中全部成員
using namespace Qin;
int main()
{printf("%d\n", a);printf("%d\n", b);return 0;
}
?C++輸入&輸出
? <iostream>是Input Output Stream的縮寫,是標準的輸?、輸出流庫,定義了標準的輸?、輸
出對象。
? std::cin是istream類的對象,它主要?向窄字符(narrow characters(of type char))的標準輸
?流。
? std::cout是ostream類的對象,它主要?向窄字符的標準輸出流。
? std::endl是?個函數,流插?輸出時,相當于插??個換?字符加刷新緩沖區。
? <<是流插?運算符,>>是流提取運算符。(C語?還?這兩個運算符做位運算左移/右移)
? 使?C++輸?輸出更?便,不需要像printf/scanf輸?輸出時那樣,需要?動指定格式,C++的輸?
輸出可以?動識別變量類型(本質是通過函數重載實現的,這個以后會講到),其實最重要的是
C++的流能更好的?持?定義類型對象的輸?輸出。
? IO流涉及類和對象,運算符重載、繼承等很多?向對象的知識,這些知識我們還沒有講解,所以這
?我們只能簡單認識?下C++IO流的?法,后?我們會有專?的?個章節來細節IO流庫。
? cout/cin/endl等都屬于C++標準庫,C++標準庫都放在?個叫std(standard)的命名空間中,所以要
通過命名空間的使??式去?他們。
? ?般?常練習中我們可以using namespace std,實際項?開發中不建議using namespace std。
? 這?我們沒有包含<stdio.h>,也可以使?printf和scanf,在包含<iostream>間接包含了。vs系列
編譯器是這樣的,其他編譯器可能會報錯。
#include<iostream>
using namespace std;int main()
{int a;double b;char c;cin >> a >> b >> c;//可以自動識別變量的類型cout << a << " " << b << " " << c << endl;return 0;
}
另外在io需求比較高的地方,如部分大量輸入的競賽題中,加上以下三行代碼可以提高C++IO效率(具體原因目前我無法解釋……)
#include<iostream>
using namespace std;int main()
{ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);return 0;
}
缺省參數
void fun(int a = 10, int b = 20, int c = 30)//全缺省
{cout << a << " " << b << " " << c << endl;
}void fun(int a, int b = 20, int c = 30)//半缺省
{cout << a << " " << b << " " << c << endl;
}
? 缺省參數是聲明或定義函數時為函數的參數指定?個缺省值。在調?該函數時,如果沒有指定實參則采?該形參的缺省值,否則使?指定的實參,缺省參數分為全缺省和半缺省參數。(有些地?把缺省參數也叫默認參數)
? 全缺省就是全部形參給缺省值,半缺省就是部分形參給缺省值。C++規定半缺省參數必須從右往左依次連續缺省,不能間隔跳躍給缺省值。
? 帶缺省參數的函數調?,C++規定必須從左到右依次給實參,不能跳躍給實參。
? 函數聲明和定義分離時,缺省參數不能在函數聲明和定義中同時出現,規定必須函數聲明給缺省值。
?之前在寫順序表的時候,每次數據滿了就需要擴容,當我們需要一次存儲1000個整型數據,那么就要擴容將近10次。如果直接這樣寫:
void STInit(St* ps, int n = 4);
在調用時可以直接把n賦值成1000。這樣一次就把空間開完了,避免了一些不必要的重復操作。?
函數重載
C++?持在同?作?域中出現同名函數,但是要求這些同名函數的形參不同,可以是參數個數不同或者類型不同。這樣C++函數調?就表現出了多態?為,使?更靈活。但是C語?是不?持同?作?域中出現同名函數的。
參數類型不同:
int Add(int a, int b)
{return a + b;
}
double Add(double a, double b)
{return a + b;
}
參數個數不同:
void fun()
{cout << "fun()" << endl;
}
void fun(int a)
{cout << "fun(int a)" << endl;
}
參數順序不同:?
void fun(int a, char b)
{cout << "fun(int a,char b)" << endl;
}
void fun(char a, int b)
{cout << "fun(char a,int b)" << endl;
}
注意:返回值不同不能作為重載條件,因為調用的方式不同,無法區分
?特殊的:下面兩個函數構成重載(參數個數不同)。
無參數時:兩個函數都可以調用,編譯器不知道調用誰。
有參數時:調用第二個缺省函數
void f()
{cout << "f()" << endl;
}void f(int a = 10)
{cout << "f(int a = 10)" << endl;
}
引用
引用的概念、定義
引?不是新定義?個變量,?是給已存在變量取了?個別名,編譯器不會為引?變量開辟內存空間,它和它引?的變量共?同?塊內存空間。?如:?壺傳中李逵,宋江叫"鐵?",江湖上?稱"?旋?";林沖,外號豹?頭;
類型&引?別名 = 引?對象;
C++中為了避免引?太多的運算符,會復?C語?的?些符號,?如前?的 << 和 >> ,這?引?也和取地址使?了同?個符號&,?家注意區分。
int main()
{int a = 0;int& b = a;int& c = a;int& d = b;++d;cout << &a << endl;cout << &b << endl;cout << &c << endl;cout << &d << endl;//地址是一樣的,因為沒有新變量,只是別名,給我記清楚了!!!return 0;
}
引用的特性
? 引?在定義時必須初始化
? ?個變量可以有多個引?
? 引??旦引??個實體,再不能引?其他實體
#include<iostream>
using namespace std;int main()
{int a = 0;int& b = a;int c = 20;b = c;//這里不是讓b引用c,C++的引用不能改變指向//這里是將c的值賦值給a(b是別名!!!)return 0;
}
引用的使用
? 引?在實踐中主要是于引?傳參和引?做返回值中減少拷?提?效率和改變引?對象時同時改變被引?對象。
? 引?傳參跟指針傳參功能是類似的,引?傳參相對更?便?些。
? 引?和指針在實踐中相輔相成,功能有重疊性,但是各有特點,互相不可替代。C++的引?跟其他語?的引?(如Java)是有很?的區別的,除了?法,最?的點,C++引?定義后不能改變指向,Java的引?可以改變指向。
之前我們經常用的交換數據的函數可以用引用這樣寫:
int Swap(int& a, int& b)
{int tmp = a;a = b;b = tmp;
}
之前我們寫鏈表尾插的時候,我們傳的是二級指針(可以改變一級指針的值),我們也可以傳一級指針的引用來改變一級指針的值,這樣就不需要傳二級指針。
void ListPushBack(LTNode** phead, int x);
void ListPushBack(LTNode*& phead, int x);
未完……