全面解析C++11新特性:現(xiàn)代編程的新起點(diǎn)
在編程語(yǔ)言的浩瀚星空中,C++ 一直是一顆耀眼的恒星,以其強(qiáng)大的性能和廣泛的應(yīng)用領(lǐng)域備受開(kāi)發(fā)者青睞。從操作系統(tǒng)到游戲開(kāi)發(fā),從嵌入式系統(tǒng)到大型數(shù)據(jù)處理,C++ 的身影無(wú)處不在。然而,隨著技術(shù)的飛速發(fā)展和編程需求的日益復(fù)雜,傳統(tǒng)的 C++ 標(biāo)準(zhǔn)逐漸顯露出一些不足。就在這時(shí),C++11 如同一位英勇的革新者,橫空出世,為 C++ 編程帶來(lái)了一場(chǎng)意義深遠(yuǎn)的變革。
C++11 不僅是對(duì)舊標(biāo)準(zhǔn)的簡(jiǎn)單升級(jí),更是一次全面的現(xiàn)代化改造。它引入了一系列實(shí)用且強(qiáng)大的新特性,涵蓋了語(yǔ)言的各個(gè)層面,從基本語(yǔ)法到內(nèi)存管理,從并發(fā)編程到泛型編程,幾乎無(wú)所不包。這些新特性極大地提升了代碼的可讀性、安全性和效率,讓開(kāi)發(fā)者能夠更加輕松地編寫(xiě)出高質(zhì)量的程序。
可以說(shuō),C++11 是現(xiàn)代編程的新起點(diǎn),它為開(kāi)發(fā)者打開(kāi)了一扇通往新世界的大門(mén)。無(wú)論是經(jīng)驗(yàn)豐富的資深程序員,還是剛剛踏入編程領(lǐng)域的新手,都能從 C++11 的新特性中受益。在接下來(lái)的內(nèi)容中,讓我們一起深入探索 C++11 的新特性,領(lǐng)略它們的魅力和強(qiáng)大之處。
一、C++ 11新特性概述
C++ 11 標(biāo)準(zhǔn)是C++98后的新標(biāo)準(zhǔn),該標(biāo)準(zhǔn)在 C++ 98 的基礎(chǔ)上修正了約 600 個(gè) C++ 語(yǔ)言中存在的缺陷,同時(shí)添加了約 140 個(gè)新特性,這些更新使得 C++ 語(yǔ)言煥然一新,這使得C++11更像是從C++98/03中孕育出的一種新語(yǔ)言,相比于C++98,C++11能更好地用于系統(tǒng)開(kāi)發(fā)和庫(kù)開(kāi)發(fā),其語(yǔ)法更加簡(jiǎn)單、穩(wěn)定和安全,不僅功能更強(qiáng)大,而且能提升程序員的開(kāi)發(fā)效率。
C++98中常使花括號(hào){}來(lái)初始化數(shù)組,而C++11擴(kuò)大了花括號(hào)括起的列表(初始化列表)的使用范圍,使其可用于所有的內(nèi)置類(lèi)型和用戶(hù)自定義的類(lèi)型,使用初始化列表時(shí),可添加等號(hào)(=),也可不添加。如:
int a={1};//內(nèi)置類(lèi)型
vector<int> v={1,2,3,4,5};//標(biāo)準(zhǔn)容器
list<string> lt{"hello","world"};//省略=號(hào)
int* arr = new int[5]={1,2,3,4,5};// 動(dòng)態(tài)數(shù)組
對(duì)象想要支持列表初始化,需給該類(lèi)(模板類(lèi))添加一個(gè)帶有initializer_list類(lèi)型參數(shù)的構(gòu)造函數(shù)即可。initializer_list是系統(tǒng)自定義的類(lèi)模板,該類(lèi)模板中主要有三個(gè)方法:begin()、end()迭代器以及獲取區(qū)間中元素個(gè)數(shù)的方法size()。如:
initializer_list<int> il{ 1,2,3,4,5 };
vector<int> v(il);//標(biāo)準(zhǔn)容器
class Vector{Vector(initializer_list<T> il){....}};//自定義類(lèi)型添加一個(gè)構(gòu)造函數(shù)
二、自動(dòng)類(lèi)型推導(dǎo):解放雙手,讓編譯器來(lái) “猜”
2.1auto關(guān)鍵字
在 C++11 之前,聲明變量時(shí)必須明確指定變量的類(lèi)型,這在一些復(fù)雜的場(chǎng)景下顯得頗為繁瑣。比如,當(dāng)使用 STL 容器的迭代器時(shí),需要寫(xiě)出冗長(zhǎng)的類(lèi)型聲明:
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::vector<int>::iterator it = numbers.begin();
而 C++11 引入的auto關(guān)鍵字,讓編譯器自動(dòng)推斷變量的類(lèi)型,大大簡(jiǎn)化了代碼:
std::vector<int> numbers = {1, 2, 3, 4, 5};
auto it = numbers.begin();
這樣不僅減少了代碼量,還降低了出錯(cuò)的概率。auto關(guān)鍵字還能與范圍 for 循環(huán)、lambda 表達(dá)式等新特性完美配合,讓代碼更加簡(jiǎn)潔和易讀 。
2.2decltype關(guān)鍵字
decltype是根據(jù)表達(dá)式的實(shí)際類(lèi)型推演出定義變量時(shí)所用的類(lèi)型,以下二種方式:
(1)推演表達(dá)式類(lèi)型作為變量的定義類(lèi)型
int a = 1,b=2;
// 用decltype推演a+b的實(shí)際類(lèi)型,作為定義c的類(lèi)型
decltype(a+b) c;
(2)推演函數(shù)返回值的類(lèi)型
int* f(int x){return &x;}
int main()
{
// 如果沒(méi)有帶參數(shù),推導(dǎo)函數(shù)的類(lèi)型
cout << typeid(decltype(f)).name() << endl;
// 如果帶參數(shù)列表,推導(dǎo)的是函數(shù)返回值的類(lèi)型,注意:此處只是推演,不會(huì)執(zhí)行函數(shù)
cout << typeid(decltype(f(1))).name() <<endl;
return 0;
}
三、智能指針:內(nèi)存管理的救星
在傳統(tǒng)的 C++ 編程中,手動(dòng)管理內(nèi)存是一件棘手的事情,容易出現(xiàn)內(nèi)存泄漏和懸空指針等問(wèn)題。C++11 引入了智能指針,為內(nèi)存管理提供了一種更加安全和便捷的方式。智能指針主要包括std::shared_ptr、std::unique_ptr和std::weak_ptr。
3.1 std::shared_ptr
std::shared_ptr是共享所有權(quán)指針,多個(gè)std::shared_ptr可以指向同一個(gè)對(duì)象,通過(guò)引用計(jì)數(shù)來(lái)管理對(duì)象的生命周期。當(dāng)引用計(jì)數(shù)為 0 時(shí),對(duì)象會(huì)被自動(dòng)釋放:
#include <memory>
#include <iostream>
int main() {
std::shared_ptr<int> sharedPtr1 = std::make_shared<int>(10);
std::shared_ptr<int> sharedPtr2 = sharedPtr1;
std::cout << "sharedPtr1 use count: " << sharedPtr1.use_count() << std::endl;
std::cout << "sharedPtr2 use count: " << sharedPtr2.use_count() << std::endl;
return 0;
}
3.2 std::unique_ptr
std::unique_ptr是獨(dú)占所有權(quán)指針,同一時(shí)間只能有一個(gè)std::unique_ptr指向?qū)ο?,它不允許拷貝和賦值,但支持移動(dòng)語(yǔ)義。std::unique_ptr在離開(kāi)作用域時(shí)會(huì)自動(dòng)釋放對(duì)象,適用于資源獨(dú)占的場(chǎng)景:
#include <memory>
#include <iostream>
int main() {
std::unique_ptr<int> uniquePtr1 = std::make_unique<int>(20);
std::unique_ptr<int> uniquePtr2 = std::move(uniquePtr1);
// uniquePtr1 現(xiàn)在為空,所有權(quán)已轉(zhuǎn)移到 uniquePtr2
if (!uniquePtr1) {
std::cout << "uniquePtr1 is empty" << std::endl;
}
if (uniquePtr2) {
std::cout << "uniquePtr2 value: " << *uniquePtr2 << std::endl;
}
return 0;
}
3.3 std::weak_ptr
std::weak_ptr是弱引用指針,它不控制對(duì)象的生命周期,主要用于解決std::shared_ptr的循環(huán)引用問(wèn)題。std::weak_ptr需要通過(guò)lock()方法轉(zhuǎn)換為std::shared_ptr來(lái)訪(fǎng)問(wèn)對(duì)象:
#include <memory>
#include <iostream>
struct Node {
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev;
};
int main() {
std::shared_ptr<Node> node1 = std::make_shared<Node>();
std::shared_ptr<Node> node2 = std::make_shared<Node>();
node1->next = node2;
node2->prev = node1;
// 使用 weak_ptr 避免循環(huán)引用
if (auto locked = node2->prev.lock()) {
std::cout << "node2's prev is valid" << std::endl;
} else {
std::cout << "node2's prev is expired" << std::endl;
}
return 0;
}
四、Lambda 表達(dá)式:匿名函數(shù)的魅力
lambda表達(dá)式實(shí)際是一個(gè)匿名函數(shù),它能簡(jiǎn)化代碼。
4.1書(shū)寫(xiě)格式
[capture-list] (parameters) mutable -> return-type { statement }
lambda表達(dá)式各部分說(shuō)明:
- [capture-list] : 捕捉列表,該列表總是出現(xiàn)在lambda函數(shù)的開(kāi)始位置,編譯器根據(jù)[]來(lái)判斷接下來(lái)的代碼是否為lambda函數(shù),捕捉列表能夠捕捉上下文中的變量供lambda函數(shù)使用。
- (parameters):參數(shù)列表。與普通函數(shù)的參數(shù)列表一致,如果不需要參數(shù)傳遞,則可以連同()一起省略
- mutable:默認(rèn)情況下,lambda函數(shù)總是一個(gè)const函數(shù),mutable可以取消其常量性。使用該修飾符時(shí),參數(shù)列表不可省略(即使參數(shù)為空)。
- ->returntype:返回值類(lèi)型。用追蹤返回類(lèi)型形式聲明函數(shù)的返回值類(lèi)型,沒(méi)有返回值時(shí)此部分可省略。返回值類(lèi)型明確情況下,也可省略,由編譯器對(duì)返回類(lèi)型進(jìn)行推導(dǎo)。
- {statement}:函數(shù)體。在該函數(shù)體內(nèi),除了可以使用其參數(shù)外,還可以使用所有捕獲到的變量。
注意: 在lambda函數(shù)定義中,參數(shù)列表和返回值類(lèi)型都是可選部分,而捕捉列表和函數(shù)體可以為空。
4.2應(yīng)用示例
int main()
{
// 最簡(jiǎn)單的lambda表達(dá)式, 無(wú)意義
[]{};
// 省略參數(shù)列表和返回值類(lèi)型,返回值類(lèi)型由編譯器推導(dǎo)為int
int a = 10, b = 20;
[=]{return a + b; };
// 省略了返回值類(lèi)型,無(wú)返回值類(lèi)型
auto fun1 = [&](int c){b = a + c; };
fun1(20);
cout<<a<<" "<<b<<endl;//a為10,b為30
// 完整的lambda函數(shù)
auto fun2 = [=, &b](int c)->int{return b += a+ c; };
cout<<fun2(10)<<endl;//結(jié)果為50
return 0;
}
4.3捕獲列表說(shuō)明
捕捉列表描述了上下文中那些數(shù)據(jù)可以被lambda使用,以及使用的方式傳值還是傳引用。
- [var]:表示值傳遞方式捕捉變量var
- [=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)
- [&var]:表示引用傳遞捕捉變量var
- [&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)
- [this]:表示值傳遞方式捕捉當(dāng)前的this指針
★注意事項(xiàng):
- 父作用域指包含lambda函數(shù)的語(yǔ)句塊
- 語(yǔ)法上捕捉列表可由多個(gè)捕捉項(xiàng)組成,并以逗號(hào)分割。
- 比如:[=, &a, &b]:以引用傳遞的方式捕捉變量a和b,值傳遞方式捕捉其他所有變量 [&,a, this]:值
- 傳遞方式捕捉變量a和this,引用方式捕捉其他變量 c. 捕捉列表不允許變量重復(fù)傳遞,否則就會(huì)導(dǎo)致編
- 譯錯(cuò)誤。 比如:[=, a]:=已經(jīng)以值傳遞方式捕捉了所有變量,捕捉a重復(fù)
- 在塊作用域以外的lambda函數(shù)捕捉列表必須為空。
- 在塊作用域中的lambda函數(shù)僅能捕捉父作用域中局部變量,捕捉任何非此作用域或者非局部變量都
- 會(huì)導(dǎo)致編譯報(bào)錯(cuò)。
- lambda表達(dá)式之間不能相互賦值,即使看起來(lái)類(lèi)型相同
4.4函數(shù)對(duì)象
函數(shù)對(duì)象,又稱(chēng)為仿函數(shù),即可以像函數(shù)一樣使用的對(duì)象,就是在類(lèi)中重載了operator()運(yùn)算符的類(lèi)對(duì)象,如庫(kù)中的less仿函數(shù):
template <class T> struct less : binary_function <T,T,bool> {
bool operator() (const T& x, const T& y) const {return x<y;}
};
在調(diào)用仿函數(shù)時(shí),可以用匿名對(duì)象調(diào)用,或者構(gòu)建一個(gè)對(duì)象來(lái)調(diào)用,如:
int main()
{
int a = 10, b = 20;
cout << "a<b?: "<<less<int>()(a, b) << endl;//匿名對(duì)象調(diào)用
less<int> l;//創(chuàng)建對(duì)象l再調(diào)用
cout << "a<b?: "<<l(a, b) << endl;
return 0;
}
五、右值引用與移動(dòng)語(yǔ)義:性能提升的秘密武器
在 C++ 中,左值和右值是兩個(gè)重要的概念。左值是指可以取地址的表達(dá)式,它通常表示一個(gè)持久的對(duì)象;右值是指不能取地址的表達(dá)式,它通常表示一個(gè)臨時(shí)的對(duì)象。在 C++11 之前,對(duì)于右值對(duì)象的處理存在一些性能問(wèn)題,因?yàn)樵诤芏嗲闆r下會(huì)對(duì)右值對(duì)象進(jìn)行不必要的拷貝。
C++11 引入了右值引用和移動(dòng)語(yǔ)義,以解決這個(gè)問(wèn)題。右值引用使用&&來(lái)表示,它只能綁定到右值對(duì)象上。移動(dòng)語(yǔ)義允許將右值對(duì)象的資源直接轉(zhuǎn)移給其他對(duì)象,而不是進(jìn)行拷貝,從而避免了不必要的開(kāi)銷(xiāo),提高了程序的性能。
5.1右值引用
(1)左值與右值一般情況下
- 普通類(lèi)型的變量,因?yàn)橛忻?,可以取地址,都認(rèn)為是左值。
- const修飾的常量,不可修改,只讀類(lèi)型的,理論應(yīng)該按照右值對(duì)待,但因?yàn)槠淇梢匀〉刂?如果只是
- const類(lèi)型常量的定義,編譯器不給其開(kāi)辟空間,如果對(duì)該常量取地址時(shí),編譯器才為其開(kāi)辟空間)。C++11認(rèn)為其是左值。
- 如果表達(dá)式的運(yùn)行結(jié)果是一個(gè)臨時(shí)變量或者對(duì)象,如C語(yǔ)言中的純右值,比如:a+b(表達(dá)式), 100(常量),將亡值。比如:表達(dá)式的中間結(jié)果、函數(shù)按照值的方式進(jìn)行返回。這些認(rèn)為是右值。
- 如果表達(dá)式運(yùn)行結(jié)果或單個(gè)變量是一個(gè)引用則認(rèn)為是左值。
(2)引用與右值引用比較
普通引用只能引用左值,不能引用右值,const引用既可引用左值,也可引用右值;C++11中右值引用,格式為類(lèi)型名+&&(如:int &&),比引用多加一個(gè)“&”:只能引用右值,一般情況不能直接引用左值。如:
int main()
{
int a = 10; //a為左值,10為右值
int& ra1 = a; // ra為a的別名
//int& ra2 = 10; // 編譯失敗,因?yàn)?0是右值
const int& ra3 = 10; //const引用右值
const int& ra4 = a; //const引用左值
int&& r1 = 10; //右值引用變量r1,編譯器產(chǎn)生了一個(gè)臨時(shí)變量,r1實(shí)際引用的是臨時(shí)變量
r1 = 0; //r1就可以被修改了
int&& r2 = a; // 編譯失敗,因?yàn)橛抑狄貌荒芤米笾? return 0;
}
5.2移動(dòng)語(yǔ)義
C++11提出了移動(dòng)語(yǔ)義概念,即:將一個(gè)對(duì)象中資源移動(dòng)到另一個(gè)對(duì)象中的方式,比如:
#include <iostream>
#include <string>
class MyString {
public:
MyString() : data(nullptr), length(0) {}
MyString(const char* str) : length(strlen(str)), data(new char[length + 1]) {
strcpy(data, str);
}
// 拷貝構(gòu)造函數(shù)
MyString(const MyString& other) : length(other.length), data(new char[length + 1]) {
strcpy(data, other.data);
}
// 移動(dòng)構(gòu)造函數(shù)
MyString(MyString&& other) noexcept : length(other.length), data(other.data) {
other.length = 0;
other.data = nullptr;
}
// 拷貝賦值運(yùn)算符
MyString& operator=(const MyString& other) {
if (this != &other) {
delete[] data;
length = other.length;
data = new char[length + 1];
strcpy(data, other.data);
}
return *this;
}
// 移動(dòng)賦值運(yùn)算符
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data;
length = other.length;
data = other.data;
other.length = 0;
other.data = nullptr;
}
return *this;
}
~MyString() {
delete[] data;
}
void print() const {
std::cout << data << std::endl;
}
private:
char* data;
size_t length;
};
MyString getString() {
return MyString("Hello, World!");
}
int main() {
MyString str1 = getString();
MyString str2 = std::move(str1);
str2.print();
return 0;
}
在這個(gè)例子中,MyString類(lèi)定義了移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符。在main函數(shù)中,getString函數(shù)返回一個(gè)臨時(shí)的MyString對(duì)象,str1通過(guò)移動(dòng)構(gòu)造函數(shù)接收這個(gè)臨時(shí)對(duì)象的資源,而不是進(jìn)行拷貝。然后,str1通過(guò)std::move將資源轉(zhuǎn)移給str2,進(jìn)一步體現(xiàn)了移動(dòng)語(yǔ)義的優(yōu)勢(shì)。
5.3右值引用引用左值
當(dāng)需要用右值引用引用一個(gè)左值時(shí),可以通過(guò)move函數(shù)將左值轉(zhuǎn)化為右值。它的功能就是將一個(gè)左值強(qiáng)制轉(zhuǎn)化為右值引用,然后實(shí)現(xiàn)移動(dòng)語(yǔ)義。如:
struct Person
{
string _name;
string _sex;
int _age;
};
int main()
{
Person p1 = { "張三","男",18 };
string&& name = move(p1._name);//用move將_name轉(zhuǎn)化為左值
return 0;
}
5.4完美轉(zhuǎn)發(fā)
看以下一段代碼:
void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int& x) { cout << "const左值引用" << endl; }
void Fun(const int&& x) { cout << "const右值引用" << endl; }
template<typename T>
void PerfectForward(T&& t) { Fun(t); }
int main()
{
PerfectForward(10); // 右值引用
int a;
PerfectForward(a); // 左值引用
PerfectForward(std::move(a)); // 右值引用
const int b = 20;
PerfectForward(b); // const左值引用
PerfectForward(std::move(b)); // const右值引用
return 0;
}
左值引用左值引用左值引用const左值引用const左值引用
它的運(yùn)行結(jié)果如上,通過(guò)結(jié)果可以看出,PerfectForward函數(shù)的參數(shù)為右值時(shí),并沒(méi)有調(diào)用對(duì)應(yīng)的參數(shù)為右值的函數(shù),可見(jiàn)編譯器將傳入的參數(shù)類(lèi)型都轉(zhuǎn)化成了左值,要想解決這種問(wèn)題,就需要用到C++11中的完美轉(zhuǎn)發(fā)了。
完美轉(zhuǎn)發(fā)是指在函數(shù)模板中,完全依照模板的參數(shù)的類(lèi)型,將參數(shù)傳遞給函數(shù)模板中調(diào)用的另外一個(gè)函數(shù)。完美轉(zhuǎn)發(fā)是目標(biāo)函數(shù)總希望將參數(shù)按照傳遞給轉(zhuǎn)發(fā)函數(shù)的實(shí)際類(lèi)型轉(zhuǎn)給目標(biāo)函數(shù),而不產(chǎn)生額外的開(kāi)銷(xiāo),就好像轉(zhuǎn)發(fā)者不存在一樣。所謂完美:函數(shù)模板在向其他函數(shù)傳遞自身形參時(shí),如果相應(yīng)實(shí)參是左值,它就應(yīng)該被轉(zhuǎn)發(fā)為左值;如果相應(yīng)實(shí)參是右值,它就應(yīng)該被轉(zhuǎn)發(fā)為右值。這樣做是為了保留在其他函數(shù)針對(duì)轉(zhuǎn)發(fā)而來(lái)的參數(shù)的左右值屬性進(jìn)行不同處理(比如參數(shù)為左值時(shí)實(shí)施拷貝語(yǔ)義;參數(shù)為右值時(shí)實(shí)施移動(dòng)語(yǔ)義)。
C++11通過(guò)forward函數(shù)來(lái)實(shí)現(xiàn)完美轉(zhuǎn)發(fā),將上面的PerfectForward函數(shù)中調(diào)用Fun的參數(shù)更改一下就可以解決,具體如下:
template<typename T>
void PerfectForward(T&& t) { Fun(std::forward<T>(t)); }
右值引用左值引用右值引用const左值引用const右值引用
這樣就根據(jù)參數(shù)類(lèi)型調(diào)用相應(yīng)的Fun函數(shù)。
右值引用作用:
- 實(shí)現(xiàn)移動(dòng)語(yǔ)義(移動(dòng)構(gòu)造與移動(dòng)賦值)
- 給中間臨時(shí)變量取別名
- 實(shí)現(xiàn)完美轉(zhuǎn)發(fā)
六、范圍 for 循環(huán):遍歷容器的新姿勢(shì)
C++11 引入的范圍 for 循環(huán)(Range-based for loop)為遍歷容器提供了一種更加簡(jiǎn)潔和安全的方式。它的語(yǔ)法如下:
for (declaration : container) {
// loop body
}
declaration用于聲明一個(gè)變量,該變量會(huì)依次綁定到container中的每個(gè)元素;container是要遍歷的容器。
下面的代碼展示了如何使用范圍 for 循環(huán)遍歷vector和數(shù)組:
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 使用范圍for循環(huán)遍歷vector
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
int arr[] = {10, 20, 30, 40, 50};
// 使用范圍for循環(huán)遍歷數(shù)組
for (int num : arr) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
范圍 for 循環(huán)不僅適用于vector、數(shù)組等容器,還適用于其他支持迭代器的容器,如list、set、map等。而且,使用范圍 for 循環(huán)可以避免傳統(tǒng) for 循環(huán)中可能出現(xiàn)的越界錯(cuò)誤,提高了代碼的安全性和可讀性。
七、其他實(shí)用新特性
除了上述特性外,C++11 還引入了許多其他實(shí)用的新特性。例如nullptr關(guān)鍵字,用于表示空指針,取代了傳統(tǒng)的NULL宏,避免了NULL在某些情況下可能引發(fā)的歧義;初始化列表,允許使用統(tǒng)一的語(yǔ)法對(duì)對(duì)象進(jìn)行初始化,提高了代碼的一致性和可讀性;constexpr關(guān)鍵字,用于聲明常量表達(dá)式,使得一些計(jì)算可以在編譯期完成,提高了程序的效率 。這些新特性雖然看似微小,但在實(shí)際編程中卻能發(fā)揮重要的作用,讓代碼更加優(yōu)雅和高效。
八、C++11經(jīng)??嫉降闹R(shí)點(diǎn)
⑴自動(dòng)類(lèi)型推斷(auto關(guān)鍵字)和范圍-based for循環(huán)區(qū)別?
自動(dòng)類(lèi)型推斷(auto關(guān)鍵字):在變量聲明時(shí)使用auto關(guān)鍵字,編譯器會(huì)根據(jù)變量的初始化表達(dá)式推斷出變量的類(lèi)型。例如:
auto x = 10; // 推斷x為整數(shù)型
auto str = "Hello"; // 推斷str為字符串型
這樣可以簡(jiǎn)化代碼,尤其對(duì)于復(fù)雜的類(lèi)型名稱(chēng)或模板類(lèi)型參數(shù)更加方便。
范圍-based for循環(huán):用于遍歷容器中的元素,不需要手動(dòng)控制迭代器。例如:
std::vector<int> numbers = {1, 2, 3, 4, 5};
for(auto num : numbers) {
std::cout << num << " ";
}
⑵范圍-based for循環(huán)會(huì)依次將容器中的每個(gè)元素賦值給迭代變量num,使得遍歷容器變得更加簡(jiǎn)潔和直觀。
C++11引入了范圍-based for循環(huán)(也稱(chēng)為foreach循環(huán)),它可以更方便地遍歷容器中的元素。使用范圍-based for循環(huán),可以自動(dòng)將容器中的每個(gè)元素賦值給迭代變量,使得遍歷容器變得更加簡(jiǎn)潔和直觀。
例如,對(duì)于一個(gè)容器vector<int>,我們可以使用范圍-based for循環(huán)來(lái)遍歷它:
std::vector<int> numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
// 對(duì)每個(gè)元素進(jìn)行操作
std::cout << num << " ";
}
上述代碼會(huì)依次將numbers中的每個(gè)元素賦值給迭代變量num,并輸出該值。通過(guò)這種方式,我們可以方便地對(duì)容器進(jìn)行遍歷操作。范圍-based for循環(huán)適用于支持迭代器或begin/end成員函數(shù)的各種容器類(lèi)型。
⑶nullptr關(guān)鍵字,用于表示空指針嗎?
是的,nullptr是C++11引入的關(guān)鍵字,用于表示空指針。它可以作為常量null的更安全和直觀的替代品,在程序中明確表示一個(gè)空指針。使用nullptr可以避免在不同上下文中可能產(chǎn)生二義性的情況,并且能夠提供更好的類(lèi)型檢查和類(lèi)型推導(dǎo)。
⑷強(qiáng)制類(lèi)型轉(zhuǎn)換新規(guī)則,如static_cast、dynamic_cast、const_cast和reinterpret_cast。
強(qiáng)制類(lèi)型轉(zhuǎn)換是在C++中用于將一個(gè)類(lèi)型的值轉(zhuǎn)換為另一種類(lèi)型。下面是四種常見(jiàn)的強(qiáng)制類(lèi)型轉(zhuǎn)換方式:
- static_cast:主要用于基本數(shù)據(jù)類(lèi)型之間的轉(zhuǎn)換,以及具有繼承關(guān)系的指針或引用之間的轉(zhuǎn)換。它在編譯時(shí)進(jìn)行類(lèi)型檢查,不提供運(yùn)行時(shí)的檢查。
- dynamic_cast:主要用于類(lèi)層次結(jié)構(gòu)中,進(jìn)行安全地向下轉(zhuǎn)型(派生類(lèi)到基類(lèi))和向上轉(zhuǎn)型(基類(lèi)到派生類(lèi))。它在運(yùn)行時(shí)進(jìn)行類(lèi)型檢查,如果無(wú)效則返回空指針(對(duì)指針)或拋出std::bad_cast異常(對(duì)引用)。
- const_cast:主要用于去除const屬性。通過(guò)const_cast可以將const對(duì)象轉(zhuǎn)換為非const對(duì)象,并且還可以通過(guò)它修改原本被聲明為const的變量。
- reinterpret_cast:這是一種較低級(jí)別和危險(xiǎn)性較高的轉(zhuǎn)換方式,它可以將任何指針或整數(shù)類(lèi)型互相轉(zhuǎn)換。它不會(huì)執(zhí)行任何特定的檢查,只是簡(jiǎn)單地重新解釋給定值所占據(jù)內(nèi)存位置的含義。
⑸Lambda表達(dá)式,用于創(chuàng)建匿名函數(shù)。
是的,Lambda表達(dá)式用于創(chuàng)建匿名函數(shù)。它提供了一種簡(jiǎn)潔的語(yǔ)法來(lái)定義并傳遞函數(shù),通常在需要使用函數(shù)作為參數(shù)或需要一個(gè)臨時(shí)函數(shù)的地方使用。
Lambda表達(dá)式的基本語(yǔ)法如下:
[捕獲列表](參數(shù)列表) -> 返回類(lèi)型 {
函數(shù)體
}
其中,
- 捕獲列表(Capture List)可以指定要在Lambda表達(dá)式中訪(fǎng)問(wèn)的外部變量。
- 參數(shù)列表(Parameter List)定義了傳遞給Lambda函數(shù)的參數(shù)。
- 返回類(lèi)型(Return Type)指定了Lambda函數(shù)的返回值類(lèi)型。
- 函數(shù)體(Function Body)包含了實(shí)際執(zhí)行的代碼。
例如,以下是一個(gè)使用Lambda表達(dá)式創(chuàng)建匿名函數(shù)并傳遞給STL算法std::for_each
的示例:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 使用Lambda表達(dá)式打印每個(gè)元素
std::for_each(numbers.begin(), numbers.end(), [](int num) {
std::cout << num << " ";
});
return 0;
}
這個(gè)Lambda表達(dá)式 [ ](int num) { std::cout << num << " "; }
接受一個(gè)整數(shù)參數(shù),并輸出該數(shù)字。在上述示例中,我們將其作為參數(shù)傳遞給std::for_each
算法以打印每個(gè)元素。
⑹移動(dòng)語(yǔ)義和右值引用(&&運(yùn)算符),用于實(shí)現(xiàn)高效的資源管理和避免不必要的拷貝構(gòu)造函數(shù)調(diào)用。
移動(dòng)語(yǔ)義和右值引用是C++11引入的特性,用于實(shí)現(xiàn)高效的資源管理和避免不必要的拷貝構(gòu)造函數(shù)調(diào)用。
移動(dòng)語(yǔ)義通過(guò)將資源的所有權(quán)從一個(gè)對(duì)象轉(zhuǎn)移到另一個(gè)對(duì)象來(lái)提高性能。在傳統(tǒng)的拷貝操作中,會(huì)先進(jìn)行深度復(fù)制,然后再銷(xiāo)毀原始對(duì)象。而移動(dòng)操作則是將原始對(duì)象的資源指針或狀態(tài)信息轉(zhuǎn)移到目標(biāo)對(duì)象中,而不進(jìn)行數(shù)據(jù)的復(fù)制。這樣可以大大減少內(nèi)存拷貝和數(shù)據(jù)處理開(kāi)銷(xiāo)。
右值引用(&&運(yùn)算符)是表示“具名值”的左值引用(&運(yùn)算符)之外的一種新類(lèi)型引用。它主要與移動(dòng)語(yǔ)義結(jié)合使用,在函數(shù)參數(shù)、返回值和賦值等場(chǎng)景中發(fā)揮作用。通過(guò)使用右值引用參數(shù),可以顯式地表達(dá)出一個(gè)臨時(shí)對(duì)象可以被移動(dòng)或接管其資源。
對(duì)于類(lèi)設(shè)計(jì)者來(lái)說(shuō),合理利用移動(dòng)語(yǔ)義和右值引用可以?xún)?yōu)化類(lèi)的性能,并避免不必要的資源拷貝。同時(shí),C++標(biāo)準(zhǔn)庫(kù)中也提供了一些支持移動(dòng)語(yǔ)義的容器、智能指針等工具,進(jìn)一步簡(jiǎn)化了資源管理。
⑺初始化列表,允許在對(duì)象初始化時(shí)使用大括號(hào)進(jìn)行成員初始化。
是的,初始化列表允許在對(duì)象初始化時(shí)使用大括號(hào)進(jìn)行成員初始化。它可以在構(gòu)造函數(shù)中使用,并且語(yǔ)法如下:
class MyClass {
public:
MyClass(int a, int b) : memberA(a), memberB(b) {
// 構(gòu)造函數(shù)的其他操作
}
private:
int memberA;
int memberB;
};
在上面的例子中,memberA
和memberB
通過(guò)初始化列表進(jìn)行初始化。這樣可以避免先創(chuàng)建對(duì)象再逐個(gè)賦值的額外開(kāi)銷(xiāo),提高了效率。同時(shí),如果成員變量是常量或引用類(lèi)型,則必須使用初始化列表進(jìn)行初始化。
⑻類(lèi)型別名與using關(guān)鍵字,用于定義自定義類(lèi)型別名。
是的,C++中可以使用typedef
關(guān)鍵字或using
關(guān)鍵字來(lái)定義自定義類(lèi)型別名。
使用typedef
關(guān)鍵字:
typedef int myInt; // 將int類(lèi)型定義為myInt類(lèi)型的別名
typedef std::vector<int> IntVector; // 將std::vector<int>定義為IntVector類(lèi)型的別名
使用using
關(guān)鍵字:
using myInt = int; // 將int類(lèi)型定義為myInt類(lèi)型的別名
using IntVector = std::vector<int>; // 將std::vector<int>定義為IntVector類(lèi)型的別名
無(wú)論使用typedef
還是using
,它們都可以用于簡(jiǎn)化復(fù)雜的類(lèi)型聲明,提高代碼可讀性。
⑼線(xiàn)程支持庫(kù)(std::thread),允許并發(fā)執(zhí)行代碼塊。
是的,std::thread是C++標(biāo)準(zhǔn)庫(kù)中提供的線(xiàn)程支持庫(kù),它允許并發(fā)執(zhí)行代碼塊。使用std::thread,你可以創(chuàng)建新的線(xiàn)程并在其中執(zhí)行指定的函數(shù)或可調(diào)用對(duì)象。這樣可以實(shí)現(xiàn)多個(gè)任務(wù)同時(shí)執(zhí)行,從而提高程序的性能和響應(yīng)性。
下面是一個(gè)簡(jiǎn)單示例:
#include <iostream>
#include <thread>
// 線(xiàn)程函數(shù)
void printMessage() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
// 創(chuàng)建新線(xiàn)程,并在其中執(zhí)行printMessage函數(shù)
std::thread t(printMessage);
// 主線(xiàn)程繼續(xù)執(zhí)行其他任務(wù)
std::cout << "Hello from main thread!" << std::endl;
// 等待子線(xiàn)程完成
t.join();
return 0;
}
上述代碼創(chuàng)建了一個(gè)新線(xiàn)程,并在該線(xiàn)程中執(zhí)行printMessage函數(shù)。同時(shí),主線(xiàn)程會(huì)打印"Hello from main thread!"。當(dāng)子線(xiàn)程完成后,使用t.join()等待子線(xiàn)程退出。
需要注意的是,在使用std::thread時(shí)需要正確管理資源和同步操作,避免競(jìng)態(tài)條件和內(nèi)存訪(fǎng)問(wèn)問(wèn)題。
⑽合理使用智能指針(如std::shared_ptr和std::unique_ptr)來(lái)管理動(dòng)態(tài)內(nèi)存分配,避免內(nèi)存泄漏和懸掛指針問(wèn)題。
智能指針是一種強(qiáng)大的工具,用于管理動(dòng)態(tài)分配的內(nèi)存,可以幫助我們避免內(nèi)存泄漏和懸掛指針問(wèn)題。
std::unique_ptr
是一種獨(dú)占所有權(quán)的智能指針。它確保只有一個(gè)指針可以訪(fǎng)問(wèn)資源,并在不再需要時(shí)自動(dòng)釋放內(nèi)存。它適合用于單個(gè)所有者場(chǎng)景,例如擁有一個(gè)對(duì)象或管理動(dòng)態(tài)分配的數(shù)組。
std::shared_ptr
是一種共享所有權(quán)的智能指針。多個(gè) shared_ptr
可以共享對(duì)同一資源的所有權(quán),并且會(huì)自動(dòng)跟蹤引用計(jì)數(shù)。只有當(dāng)最后一個(gè) shared_ptr
釋放資源時(shí),內(nèi)存才會(huì)被釋放。這使得 std::shared_ptr
特別適用于需要共享資源所有權(quán)的場(chǎng)景。
使用智能指針可以有效地管理動(dòng)態(tài)內(nèi)存,并且不容易出現(xiàn)內(nèi)存泄漏或懸掛指針問(wèn)題。但要注意,在使用 std::unique_ptr
時(shí)要避免循環(huán)引用,而在使用 std::shared_ptr
時(shí)要考慮引起性能開(kāi)銷(xiāo)和潛在的死鎖風(fēng)險(xiǎn)。