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