目录
C++11新特性详解
C++11是C++语言的一个重要里程碑,它在2011年被正式确定为国际标准。相比于之前的C++98/03标准,C++11引入了大量的新特性,从列表初始化、类型推导、智能指针到多线程支持,C++11为现代C++编程提供了强大的工具和支持,使得C++语言在现代编程中更加强大和灵活。本文将详细介绍C++11的一些关键新特性。
1. C++11简介
在2003年,C++标准委员会提交了一份技术勘误表(TC1),使得C++03成为C++98之后的最新标准。然而,C++03主要是对C++98标准中的漏洞进行修复,语言的核心部分并没有大的改动。因此,人们通常将这两个标准合并称为C++98/03标准。从C++0x到C++11,C++标准经过了长达十年的发展,带来了约140个新特性和对C++03标准中约600个缺陷的修正。这使得C++11更像是从C++98/03中孕育出的一种新语言。
2. 统一的列表初始化
在C++98中,花括号{}
只能用于数组或结构体元素的初始化。
struct Point {
int _x;
int _y;
};
int main() {
int array1[] = {1, 2, 3, 4, 5};
int array2[5] = {0};
Point p = {1, 2};
return 0;
}
C++11扩大了初始化列表的使用范围,使其适用于所有内置类型和用户自定义类型。使用初始化列表时,可以添加等号=
,也可以不添加。
Point p{1, 2}; // 用户自定义类型对象的初始化 OK
3. std::initializer_list
std::initializer_list
是C++11中引入的一个模板类,元素数组的引用,用于封装初始化列表的语法结构,使得初始化容器对象更加方便。
#include <initializer_list>
class Matrix {
public:
Matrix(std::initializer_list<double> init) {
for (auto value : init) {
// 处理初始化数据
}
}
};
int main() {
Matrix m = {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}};
}
注意1:C++11后,STL中的容器,如vector
、list
、map
等都增加了接受std::initializer_list
作为参数的构造函数。
int main() {
vector<int> v = {1, 2, 3, 4};
list<int> lt = {1, 2};
map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
return 0;
}
注意2:std::initializer_list
类型不能使用引用传参,因为内部实现已经使用引用提升效率
Matrix(std::initializer_list<double>& init) { // <- 错误!
for (auto value : init) {
// 处理初始化数据
}
}
4. auto和decltype
C++98中,auto
用于声明具有自动存储期的变量,但是局部变量默认就是具有自动存储期的,因此auto
关键字在C++98中的作用并不大。
C++11废除了C++98中auto的定义,引入了新的auto
和decltype
两个关键字,用于简化类型声明。
auto
用于自动类型推导,要求必须进行显示初始化。(在STL复杂变量声明中好用)decltype
将变量的类型声明为表达式指定的类型。
int main() {
int i = 10;
auto p = &i; // p的类型是int*
list<int> l = {1,2,3,4};
auto pl = l.begin();// pl的类型是list迭代器类型
decltype(i) j = i; // j的类型是int
return 0;
}
5. nullptr
NULL
在C++中的问题主要源于它与整数 0
的等价性,这可能导致类型混淆和逻辑错误。为了解决NULL
被定义成字面量0带来的问题,C++11引入了nullptr
,用于表示空指针。
int main() {
int* p = nullptr;
return 0;
}
6. 范围for循环
范围for循环是C++11中引入的一种新的循环方式,它可以直接迭代容器中的元素。
int main() {
vector<int> v = {1, 2, 3, 4};
for (auto& e : v)
{
cout << e << ' ';
}
return 0;
}
笔记:范围for底层由迭代器实现,自定义类型实现迭代器后也可以使用范围for遍历内部元素。
// 范围for的内部实现
for (auto it = v.begin(); it != v.end(); ++it)
{
cout << *it << endl;
}
7. 智能指针
C++11引入了智能指针,用于自动管理动态分配的内存。智能指针主要有三种:unique_ptr
、shared_ptr
和weak_ptr
。
int main() {
unique_ptr<int> p1(new int(10));
shared_ptr<int> p2(new int(20));
weak_ptr<int> p3(p2);
return 0;
}
单独写了篇博客~链接如下:
C++ | 一文读懂C++11中的智能指针(附模拟实现):C++内存管理的现代解决方案-CSDN博客
8. 新增加的容器
C++11增加了一些新的STL容器,如array
、forward_list
和unordered_map
等。
int main() {
array<int, 5> arr = {1, 2, 3, 4, 5};
forward_list<int> fl = {1, 2, 3};
unordered_map<string, int> um = {{"one", 1}, {"two", 2}};
return 0;
}
附上接口链接~:
http://www.cplusplus.com/reference/vector/vector/emplace_back/
http://www.cplusplus.com/reference/vector/vector/push_back/
http://www.cplusplus.com/reference/map/map/insert/
http://www.cplusplus.com/reference/map/map/emplace/
9. 默认成员函数控制
C++11允许开发者更好地控制默认成员函数的生成。可以使用default
关键字强制生成默认函数,使用delete
关键字禁止生成默认函数。
class Person {
public:
Person(const char* name = "", int age = 0) : _name(name), _age(age) {}
Person(const Person& p) = default;
Person& operator=(const Person& p) = delete;
private:
bit::string _name;
int _age;
};
笔记:C++11前进行特殊类设计,可以将函数声明为private达到delete的效果,然后提供public接口获取相关资源。
C++ | 深入理解C++中的特殊类设计和单例模式(懒汉模式、饿汉模式)-CSDN博客
10. 右值引用、移动语义、完美转发和bind绑定
右值引用和移动语义
C++11引入了右值引用和移动语义,用于提高资源的利用效率。
- 右值引用允许对即将被销毁的对象进行引用,从而实现资源的转移。
- 移动构造函数和移动赋值运算符允许在对象间转移资源,而不是进行复制。
class string {
public:
string(string&& s) : _str(s._str), _size(s._size), _capacity(s._capacity) {
s._str = nullptr;
s._size = 0;
s._capacity = 0;
}
string& operator=(string&& s) {
_str = s._str;
_size = s._size;
_capacity = s._capacity;
s._str = nullptr;
s._size = 0;
s._capacity = 0;
return *this;
}
private:
char* _str;
size_t _size;
size_t _capacity;
};
完美转发/万能引用
std::forward 完美转发在传参的过程中保留对象原生类型属性
#include<iostream>
using namespace std;
void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }
// 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
template<typename T>
void PerfectForward(T && t) // 可以接收任何类型的变量
{
Fun(forward<T>(t)); // 根据传入参数的类型,决定调用哪个版本的Fun
}
int main()
{
int a = 10;
PerfectForward(10);
PerfectForward(a);// 左值
PerfectForward(std::move(a)); // 右值
const int b = 8;
PerfectForward(b);// const 左值
PerfectForward(std::move(b)); // const 右值
return 0;
}
解释:
1、模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
2、接收变量t后 t坍塌为左值。
3、完美转发forward自动识别t的类型,如果t原本就是左值不做处理,如果为右值则move之后传递给下层
bind绑定
std::bind
是 C++ 标准库中 <functional>
头文件提供的一个模板函数,它用于创建一个新的可调用对象 —— 通常是一个函数对象或 lambda 表达式 —— 的绑定版本。这个绑定版本可以固定原函数对象的某些参数,从而产生一个新的函数对象,这个新函数对象可以接受剩余的参数。
定义:std::bind(fn, arg1, arg2, ..., argN)
fn
是要绑定的可调用对象,可以是普通函数、成员函数、函数对象或 lambda 表达式。arg1
到argN
是要绑定的参数,它们将被固定在新创建的函数对象中。std::bind
还使用特殊的参数占位符_1
,_2
,_3
, ... 来代表新函数对象将要接收的参数。这些占位符在调用新函数对象时,会被实际的参数替换。
#include<iostream>
#include<functional> // bind函数头文件
#include<cmath>
using namespace std;
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
using placeholders::_4;
int main()
{
auto fun1 = [](int x, double money, double percent){
return money*pow((percent+1), x);
};
// 使用 std::bind 绑定函数参数
// _1表示这个参数是fun_1year的第一个参数
auto fun_1year = std::bind(fun1, 1, _1, 0.01);
auto fun_3year = std::bind(fun1, 3, _1, 0.015);
auto fun_5year = std::bind(fun1, 5, _1, 0.02);
auto fun_10year = std::bind(fun1, 10, _1, 0.025);
// 计算并输出不同年限的投资增长
cout << "1年后: " << fun_1year(10000) << endl;
cout << "3年后: " << fun_3year(10000) << endl;
cout << "5年后: " << fun_5year(10000) << endl;
cout << "10年后: " << fun_10year(10000) << endl;
return 0;
}
11. lambda表达式
C++11引入了lambda表达式,允许在代码中定义匿名函数。lambda函数在很多时候比仿函数好用(比如自定义sort比较函数)。
int main() {
vector<Goods> v = {{"苹果", 2.1, 5}, {"香蕉", 3, 4}, {"橙子", 2.2, 3}, {"菠萝", 1.5, 4}};
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._price < g2._price;
});
return 0;
}
12. 线程库
C++11引入了对多线程的支持,使得并行编程更加容易。
int main() {
thread t1([]() { cout << "Thread1" << endl; });
thread t2([]() { cout << "Thread2" << endl; });
t1.join();
t2.join();
return 0;
}