C++特性和新特性
C++11
C++11是C++編程語言的一個重要標準版本,是C++98標準發布后13年來的第一次重大修正,它引入了許多新特性和改進,極大地增強了C++語言的表達能力和開發效率。
C++11是C++編程語言的一個重要標準版本,由國際標準化組織(ISO)和國際電工委員會(IEC)旗下的C++標準委員會(ISO/IEC JTC1/SC22/WG21)于2011年8月12日公布,并于2011年9月正式出版,其標準文件號為ISO/IEC 14882:2011。C++11是C++98標準發布后13年來的第一次重大修正,它引入了許多新特性和改進,極大地增強了C++語言的表達能力和開發效率。以下是對C++11的一些主要特性和改進的歸納:
核心語言的新機能
自動類型推導(auto):
C++11引入了auto關鍵字,用于自動推導變量的類型,使得代碼更加簡潔,特別是在處理復雜類型或模板類型時。
#include <iostream>
#include <vector> int main() { // 使用auto推導vector<int>類型 auto myVector = std::vector<int>{1, 2, 3, 4, 5}; // 遍歷vector for(auto it = myVector.begin(); it != myVector.end(); ++it) { std::cout << *it << std::endl; } // 推導函數返回類型 auto result = myVector.size(); // result的類型是std::vector<int>::size_type,通常是unsigned int return 0;
}
decltype:
與auto類似,但decltype用于推導表達式的類型,適用于auto無法使用的場景。
#include <iostream> int main() { int x = 5; // 使用decltype獲取x的類型,并聲明同類型的變量y decltype(x) y = x; // 也可以用于復雜表達式 int a = 1, b = 2; decltype(a + b) c = a + b; // c的類型是int // 引用類型 int& ref = x; decltype(ref) anotherRef = y; // anotherRef也是int&類型 // 注意,decltype(表達式)的結果取決于表達式的形式 decltype((x)) wholeX = x; // wholeX是int&,因為表達式(x)是左值 return 0;
}
右值引用和移動語義:
引入了右值引用(T&&)和移動語義,允許臨時對象(右值)的資源被“竊取”以進行高效的資源轉移,避免了不必要的拷貝操作。
- 右值引用:右值引用是C++11中引入的一個關鍵特性,它允許程序員顯式地將一個表達式標記為右值,從而可以利用移動語義進行優化。在C++中,每個表達式都可以被分類為左值或右值。左值是指那些可以取地址的表達式,如變量、數組元素等,而右值則是指那些不能取地址的表達式,如字面量、臨時變量、表達式求值結果等。右值引用就是用來引用這些右值的類型,其語法是在變量名前添加兩個連續的“&”符號,如“int&&”。
int&& rvalueRef = 10; // 正確:10是右值,可以被右值引用綁定
int x = 10;
int&& rvalueRef2 = std::move(x); // 正確:使用std::move將x轉換為右值
- 移動語義:移動語義是C++11中引入的一種新的語言特性,旨在提高程序的性能和資源管理效率。其核心概念在于允許對象間資源的轉移,而非傳統的拷貝操作。移動語義通過右值引用和移動構造函數(以及移動賦值運算符)實現了資源的所有權從一個對象到另一個對象的轉移,從而避免了不必要的復制操作。
- 移動構造函數:接收一個右值引用參數,并將其資源“移動”到新的對象中,而不是復制這些資源。移動構造函數的定義形式為
ClassName(ClassName&& other);
。 - 移動賦值函數:用于將一個對象的資源轉移給另一個已經存在的對象,其定義形式通常為
ClassName& operator=(ClassName&& other);
。
class MyClass {
public: MyClass(int* data) : ptr(data) {} MyClass(MyClass&& other) noexcept : ptr(other.ptr) { other.ptr = nullptr; // 確保原對象不再擁有資源 } MyClass& operator=(MyClass&& other) noexcept { if (this != &other) { delete[] ptr; // 釋放原資源 ptr = other.ptr; other.ptr = nullptr; // 確保原對象不再擁有資源 } return *this; } ~MyClass() { delete[] ptr; } private: int* ptr;
}; MyClass createMyClass() { return MyClass(new int[100]); // 返回臨時對象,將觸發移動語義
} MyClass obj = createMyClass(); // 這里將調用移動構造函數
統一的初始化:
C++11引入了統一的初始化語法({}),使得所有類型的對象都可以使用相同的初始化方式。通過大括號{}
和std::initializer_list
提供了更加靈活、安全、直觀的初始化方法。
- 使用大括號
{}
直接初始化- 對于基本數據類型:
int a{10};
- 對于數組:
int arr[3]{1, 2, 3};
或者int arr[3] = {1, 2, 3};
(注意,這里=
并不是傳統意義上的拷貝初始化,而是C++允許的一種簡寫形式,仍然屬于統一初始化) - 對于結構體和類對象:
struct Point{int x, y;} p{1, 2};
- 對于容器:
std::vector<int> vec{1, 2, 3};
- 對于基本數據類型:
- 使用
std::initializer_list
- 在C++11中,許多容器(如
std::vector
、std::map
等)都增加了接受std::initializer_list
作為參數的構造函數,使得容器初始化更加方便。 std::initializer_list
是一個輕量級的、可以容納固定數量元素的容器,它在初始化時自動生成,并在初始化結束后銷毀。
- 在C++11中,許多容器(如
Lambda表達式:
提供了一種定義匿名函數對象的方式,使得編寫回調函數等更加簡潔方便。Lambda 表達式特別適用于需要函數對象但又不想正式命名一個函數的場景,比如作為算法(如 std::sort
)的參數,或者在需要回調函數的地方。
[capture](parameters) mutable -> return_type { // 函數體
}
- 捕獲列表(
[capture]
):指定哪些外部變量在 lambda 表達式內部是可見的。如果省略捕獲列表,則 lambda 表達式不能訪問任何外部變量。捕獲列表可以是值捕獲(通過拷貝)或引用捕獲(通過引用)。 - 參數列表(
(parameters)
):與普通函數的參數列表相同,定義了 lambda 表達式的參數。如果 lambda 表達式不接受任何參數,則可以省略參數列表。 - mutable 關鍵字(可選):允許在 lambda 表達式體內修改被捕獲的變量的值(如果它們是通過值捕獲的)。默認情況下,這些變量在 lambda 表達式內是不可變的。
- 返回類型(
-> return_type
):指定 lambda 表達式的返回類型。如果 lambda 表達式體只包含一個返回語句,并且編譯器可以推導出返回類型,則可以省略返回類型。 - 函數體:定義了 lambda 表達式的操作。
- 一個計算兩數之和的簡單例子:
#include <iostream>
#include <algorithm>
#include <vector> int main() { int a = 10, b = 20; // 定義一個 lambda 表達式,計算兩個整數的和 auto sum = [](int x, int y) { return x + y; }; // 使用 lambda 表達式 std::cout << "The sum is " << sum(a, b) << std::endl; // 使用 lambda 表達式作為 std::sort 的比較函數 std::vector<int> vec = {4, 1, 3, 5, 2}; std::sort(vec.begin(), vec.end(), [](int x, int y) { return x < y; }); for (int n : vec) { std::cout << n << ' '; } std::cout << std::endl; return 0;
}
標準庫的擴展
智能指針:
C++11標準庫增加了shared_ptr、unique_ptr和weak_ptr等智能指針,用于自動管理動態分配的內存,減少內存泄漏的風險。
- unique_ptr
- 獨占式智能指針,確保只有一個指針可以指向資源。
- 通過
std::move()
函數可以轉移資源的所有權。
#include <iostream>
#include <memory> class MyClass {
public: MyClass(int value) : value_(value) {} void print() const { std::cout << "Value: " << value_ << std::endl; } private: int value_;
}; int main() { // 創建一個 unique_ptr 指向 MyClass 的實例 std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>(10); // 使用 ptr ptr->print(); // 當 ptr 離開作用域時,它指向的 MyClass 實例將被自動銷毀 return 0;
}
- shared_ptr
- 共享式智能指針,允許多個指針共享同一個資源。
- 采用引用計數機制,當所有shared_ptr對象都不再需要該資源時,資源會自動被銷毀。
//Myclass同上
int main() { // 創建兩個 shared_ptr 指向同一個 MyClass 實例 std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>(20); std::shared_ptr<MyClass> ptr2 = ptr1; // ptr2 和 ptr1 共享所有權 // 使用 ptr1 和 ptr2 ptr1->print(); ptr2->print(); // 當 ptr1 和 ptr2 都離開作用域時,MyClass 實例將被銷毀 return 0;
}
- weak_ptr
- 弱引用智能指針,用于輔助shared_ptr工作,不增加資源的引用計數。
- 當所有的shared_ptr對象都不再需要該資源時,weak_ptr對象會自動失效。
//Myclass同上
int main() { // 創建一個 shared_ptr 和一個 weak_ptr std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>(30); std::weak_ptr<MyClass> weakPtr = ptr; // 使用 ptr ptr->print(); // 嘗試通過 weakPtr 訪問資源,需要先鎖定 weakPtr if (auto lockedPtr = weakPtr.lock()) { lockedPtr->print(); } else { std::cout << "weak_ptr is expired!" << std::endl; } // 當 ptr 離開作用域時,MyClass 實例將被銷毀 // weakPtr 將自動變為過期狀態 return 0;
}
無序容器:
新增了unordered_map和unordered_set等基于哈希表的容器,提供了比原有map和set更高的查找效率。點擊了解更多容器知識:STL標準模板庫容器操作集合-CSDN博客
std::unordered_map
:存儲鍵值對,其中每個鍵都是唯一的。鍵和值可以是任何可復制且可賦值的類型。std::unordered_multimap
:與unordered_map
類似,但允許鍵重復。std::unordered_set
:只存儲鍵的集合,鍵是唯一的。std::unordered_multiset
:與unordered_set
類似,但允許鍵重復。
正則表達式:
標準庫增加了對正則表達式的支持,使得字符串處理更加靈活和強大。從C++11開始,C++標準庫引入了<regex>
頭文件,提供了對正則表達式的支持。C++中的正則表達式類和相關函數主要包括:
- std::regex:定義包含正則表達式的對象。
- std::smatch和std::cmatch:定義保存匹配結果的對象,分別用于string類型和char*類型的字符串。
- 常用正則匹配函數
- std::regex_match:判斷整個目標字符串是否與正則表達式完全匹配。
- std::regex_search:在目標字符串中搜索與正則表達式匹配的第一個子字符串。
- std::regex_replace:用指定的字符串替換與正則表達式匹配的部分。
#include <iostream>
#include <regex>
#include <string> int main() { std::string s = "hello, world!"; std::regex e("\\bhello\\b"); // 匹配單詞"hello" if (std::regex_search(s, e)) { std::cout << "Match found!" << std::endl; } else { std::cout << "No match." << std::endl; } // 替換匹配到的內容 std::string replaced = std::regex_replace(s, e, "Hi"); std::cout << replaced << std::endl; // 輸出: "Hi, world!" return 0;
}
線程支持:
C++11首次在標準庫中引入了線程支持,包括線程(std::thread)、互斥鎖(std::mutex)、條件變量(std::condition_variable)等,使得C++能夠更方便地進行多線程編程。以下是C++11中線程支持的主要特點:
- std::thread類
std::thread
是C++11中用于表示線程的類。通過創建std::thread
的實例并傳遞給它一個可調用對象(如函數、lambda表達式、函數對象等),可以啟動一個新的線程來執行該可調用對象。std::thread
的構造函數有多種重載形式,允許傳遞不同數量和類型的參數給線程函數。
- 線程控制
- join():等待線程結束。調用線程(通常是主線程)會阻塞,直到被join的線程執行完畢。join操作是線程同步的一種方式。
- detach():分離線程,使其獨立于主線程運行。一旦線程被分離,就不能再對其執行join操作。分離后的線程在結束時會自動釋放資源。
- joinable():檢查線程是否可以被join。如果線程已經被join或detach,或者線程對象從未與任何執行線程關聯,則joinable()返回false。
- 線程ID
- 每個
std::thread
對象都有一個唯一的標識符,可以通過調用get_id()
成員函數來獲取。這個ID可以用于區分不同的線程。
- 每個
- 線程互斥與同步
- C++11還引入了
<mutex>
、<condition_variable>
等頭文件,提供了互斥鎖、條件變量等同步機制,用于解決多線程中的數據競爭和同步問題。
- C++11還引入了
#include <iostream>
#include <thread> //在這個示例中,我們創建了兩個線程t1和t2,它們分別執行threadFunction函數,并傳遞不同的參數。然后,主線程通過調用join()函數等待這兩個線程結束。
void threadFunction(int n) { for (int i = 0; i < 5; ++i) { std::cout << "Thread: " << n << ", Count: " << i << std::endl; }
} int main() { std::thread t1(threadFunction, 1); std::thread t2(threadFunction, 2); // 等待兩個線程結束 t1.join(); t2.join(); return 0;
}
其他重要特性
nullptr:
引入了nullptr作為空指針的字面量,替代了原來的NULL宏,提高了代碼的安全性和可讀性。
nullptr
是一個特殊的關鍵字,其類型是std::nullptr_t
。這個類型只能被隱式轉換為指針類型,而不能被轉換為整數類型,從而避免了類型不匹配的問題。- 相比之下,
NULL
通常被定義為0
或((void*)0)
,可以隱式地轉換為任何指針類型或整數類型,這可能導致意外的類型轉換錯誤。
基于范圍的for循環:
提供了一種更簡潔的遍歷容器或數組的方式,使得代碼更加簡潔易讀。
for (declaration : expression) { // 循環體
}
- 元素類型:在
declaration
中聲明的類型應該與容器中元素的類型相匹配,或者至少是容器中元素類型的可隱式轉換類型。 - 修改元素:如果你需要在循環中修改元素的值,并且這個修改對容器是可見的,你應該使用元素的引用(通過
&
)來聲明變量。例如:
for (int& num : nums) { num *= 2; // 將會修改容器中的元素
}
例子
#include <iostream>
#include <vector> int main() { std::vector<int> nums = {1, 2, 3, 4, 5}; // 使用基于范圍的for循環遍歷vector for (int num : nums) { std::cout << num << " "; } return 0;
}
變長參數模板:
允許模板參數的數量在編譯時確定,為泛型編程提供了更強大的能力。(沒看懂,以后再說)
constexpr:
允許在編譯時計算表達式的值,并用于常量表達式的定義,提高了程序的運行效率。使用 constexpr
可以提高程序的性能,因為它允許編譯器在編譯時進行更多的優化,而不是在運行時計算表達式的值。
- 變量:當用于變量時,
constexpr
變量必須在聲明時初始化,并且其值必須是編譯時常量。這意味著它不能依賴于運行時才能確定的值,如用戶輸入或文件讀取。
constexpr int max_value = 100; // 正確:編譯時常量
// constexpr int x = get_value_from_user(); // 錯誤:不是編譯時常量 // 可以在編譯時計算
constexpr int square(int x) { return x * x;
} constexpr int result = square(5); // 正確:result 的值是 25,在編譯時確定
- 函數:當用于函數時,
constexpr
函數表示該函數可以在編譯時求值,但并非所有constexpr
函數都必須在編譯時調用。如果一個constexpr
函數在編譯時沒有被用于需要常量表達式的上下文中,它也可以像普通函數一樣在運行時被調用。
constexpr int factorial(int n) { return n <= 1 ? 1 : n * factorial(n - 1);
} int main() { constexpr int value = factorial(5); // 在編譯時計算 std::cout << "Factorial of 5 is " << factorial(10) << std::endl; // 在運行時計算 return 0;
}
C++與Java
以下是對C++和Java更詳細的比較,包括語法、跨平臺性、性能、安全性以及各自的優勢和應用場景等方面的詳細分析:
一、語法和風格
C++
- 面向對象:C++是一種支持面向對象的編程語言,但它也支持面向過程和泛型編程。
- 復雜性:C++的語法相對復雜,包含指針、手動內存管理、多重繼承等特性,這使得它在學習和使用時需要更多的注意和技巧。
- 靈活性:由于C++提供了對底層硬件的直接訪問能力,它允許開發者編寫高度優化的代碼,但同時也需要開發者對內存和資源的管理有深入的理解。
Java
- 面向對象:Java是一種純面向對象的編程語言,所有的代碼都封裝在類中。
- 簡潔性:Java的語法比C++更簡單、更直觀,易于學習和使用。它自動管理內存,減少了內存泄漏和指針錯誤的風險。
- 平臺無關性:Java的“一次編寫,到處運行”特性使得它可以在任何支持Java虛擬機(JVM)的平臺上運行,無需修改代碼。
二、跨平臺性
C++
- 跨平臺編譯:C++可以通過編寫可移植的代碼或使用跨平臺庫來實現跨平臺編譯和運行,但這需要開發者對不同的操作系統有一定的了解,并編寫相應的適配代碼。
- 平臺依賴:在某些情況下,C++程序可能依賴于特定的操作系統或硬件特性,這限制了其跨平臺能力。
Java
- 自動跨平臺:Java通過字節碼和Java虛擬機(JVM)實現了真正的跨平臺能力。Java程序在任何安裝了JVM的平臺上都可以運行,無需任何修改。
- 廣泛支持:由于JVM的廣泛部署和支持,Java程序幾乎可以在所有主流操作系統上運行。
三、性能
C++
- 高性能:C++程序通常比Java程序具有更高的性能,因為它更接近底層硬件,且提供了更多的控制。
- 編譯型語言:C++是編譯型語言,代碼在編譯時會被轉換成機器碼,這使得它可以在運行時直接執行,減少了運行時的開銷。
Java
- 優化性能:盡管Java在性能上不如C++,但通過即時編譯器(JIT)和其他優化技術,Java程序的性能已經非常接近甚至在某些情況下超過C++。
- 內存管理開銷:Java的自動內存管理和垃圾回收機制雖然簡化了編程過程,但也帶來了一定的性能開銷。
四、安全性
C++
- 潛在風險:C++的復雜性使得它更容易出現安全問題,如緩沖區溢出、內存泄漏等。開發者需要更加小心地管理資源和避免常見的編程錯誤。
- 直接硬件訪問:C++的直接硬件訪問能力在帶來性能優勢的同時,也增加了被惡意利用的風險。
Java
- 較高安全性:Java通過其自動內存管理、強類型檢查和運行時環境等特性提供了相對較高的安全性。
- 沙箱模型:Java的沙箱模型限制了程序對系統資源的訪問,進一步提高了安全性。
五、優勢和應用場景
C++
- 優勢:高性能、直接硬件訪問、豐富的庫和生態系統、多范式編程支持。
- 應用場景:系統級編程(如操作系統、驅動程序)、游戲開發、嵌入式系統、高頻交易等。
Java
- 優勢:跨平臺性、自動內存管理、豐富的類庫和框架、強大的社區支持。
- 應用場景:企業級應用(如ERP、CRM)、Web開發(如Servlet、JSP)、移動應用開發(如Android)、大數據處理(如Hadoop)等。
C++和Python
C++和Python是兩種非常流行的編程語言,它們各自具有獨特的特性和優勢,適用于不同的編程場景和需求。以下是對C++和Python的詳細比較和歸納:
C++
性能與效率
- 編譯型語言:C++是一種編譯型語言,其代碼被編譯成機器碼后直接在計算機上執行,因此執行速度通常比解釋型語言快。
- 高效性:C++編譯生成的機器碼效率高,可以直接與硬件進行交互,適合在性能要求較高的應用中使用。
底層控制
- 內存管理:C++允許程序員直接管理內存,包括分配和釋放內存,這提供了更大的靈活性和性能優勢,但也增加了內存泄漏和懸掛指針的風險。
- 指針操作:C++支持指針操作,允許直接訪問內存地址,這在系統編程和嵌入式系統開發中非常有用。
面向對象編程
- 支持面向對象編程:C++支持面向對象編程,允許使用類和對象來組織代碼,提高了代碼的可重用性和可維護性。
- 多范式編程:除了面向對象編程外,C++還支持泛型編程和函數式編程等不同的編程范式。
標準庫與移植性
- 豐富的標準庫:C++標準庫提供了豐富的數據結構和算法,幫助開發者更容易地實現各種功能。
- 良好的移植性:C++編寫的程序可以在多個平臺上編譯和運行,具有很好的移植性。
復雜性與學習曲線
- 相對復雜:C++的語法相對較為復雜,需要程序員更加關注內存管理和指針操作,因此學習曲線較陡。
- 開發速度:由于需要手動管理內存和進行更多的底層操作,相比Python等高級語言,C++的開發速度通常較慢。
Python
簡潔易讀
- 簡潔的語法:Python以其簡潔優美的語法而聞名,代碼量通常更少,更易于閱讀和理解。
- 動態類型:Python是一種動態類型語言,在運行時可以根據賦值來自動推斷變量的類型,增加了代碼的靈活性。
高級特性
- 自動內存管理:Python使用垃圾回收機制自動管理內存,開發者無需操心內存釋放的問題,這使得Python編程更加安全和簡單。
- 支持多種編程范式:Python支持面向對象編程、函數式編程等高級特性,提供了更高的開發效率。
豐富的庫與生態系統
- 龐大的標準庫和第三方庫:Python擁有龐大且活躍的社區,提供了豐富的第三方庫和工具,使得開發者能夠快速解決各種問題。
- 廣泛的應用領域:Python廣泛用于Web開發、數據分析、人工智能、科學計算等領域,是人工智能領域的主要編程語言之一。
開發速度與易用性
- 易學易用:Python的語法簡單清晰,易于學習和閱讀,適合初學者入門編程。
- 快速開發:Python的簡潔性和豐富的庫使得開發者能夠快速編寫和測試代碼,提高開發效率。