目录
1、结构化绑定(c++17)
返回多个返回值。
#include <iostream>
#include <tuple>
std::tuple<int, double, std::string> fun()
{
return std::make_tuple(21, 2.1, "string");
}
int main()
{
auto [x, y, z] = fun();
std::cout << " x = " << x <<std::endl;
std::cout << " y = " << y << std::endl;
std::cout << " z = " << z << std::endl;
return 0;
}
2、auto关键字
常用的使用地方是迭代器:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> vec = {1, 2, 3};
for (auto it = vec.begin(); it != vec.end(); ++it)
{
std::cout << *it << std::endl;
}
return 0;
}
其他常见的用法:
auto i = 5; // i 被推导为 int
auto arr = new auto(10); // arr 被推导为 int *
从c++20起,auto可以用于修饰函数形参:
int add(auto x, auto y)
{
return x + y;
}
注意,auto不能用于推导数组类型。
3、区间for迭代
#include <iostream>
#include <vector>
int main()
{
std::vector<int> vec = {1, 2, 3};
for (auto k : vec)
{
std::cout << k << std::endl;
}
return 0;
}
4、委托构造(c++11)
#include <iostream>
#include <vector>
class Base
{
public:
int value1;
int value2;
public:
Base()
{
value1 = 1;
}
Base(int value) : Base()
{
// 委托 Base() 构造函数
value2 = value;
}
};
int main()
{
Base b(2);
std::cout << b.value1 << std::endl;
std::cout << b.value2 << std::endl;
return 0;
}
5、lambda表达式
格式如下:
capture:捕捉外部变量列表,可以是[],[=],[&],[]表示不捕捉任何外部变量,[=]表示以值的方式捕捉外部变量,当在lambda中不需要修改外部变量的时候,应该使用[=],顾名思义,[&]即以引用的方式捕捉外部变量,可以在lambda中修改外部变量。
parameters:形参,这个跟普通函数的形参是一样的。
mutable:修饰符,可以取消Lambda的常量属性,因为Lambda默认是const属性;multable仅仅是让Lamdba函数体修改值传递的变量,但是修改后并不会影响外部的变量。
return-type:返回值,同普通函数的返回值。
#include <iostream>
int main()
{
auto func = [](int x, int y)->int{
return x + y;
};
int sum = func(1, 2);
std::cout << sum << std::endl;
return 0;
}
6、容器
std::array
与std::vector
不同,std::array
对象的大小是固定的,如果容器大小是固定的,那么可以优先考虑使用 std::array
容器。 另外由于 std::vector
是自动扩容的,当存入大量的数据后,并且对容器进行了删除操作, 容器并不会自动归还被删除元素相应的内存,这时候就需要手动运行 shrink_to_fit()
释放这部分内存。
std::array<int, 4> arr = {1, 2, 3, 4};
std::forward_list
std::forward_list
是一个列表容器,使用方法和 std::list
基本类似,区别是和 std::list
的双向链表的实现不同,std::forward_list
使用单向链表进行实现, 提供了 O(1)
复杂度的元素插入,不支持快速随机访问(这也是链表的特点), 也是标准库容器中唯一一个不提供 size()
方法的容器。当不需要双向迭代时,具有比 std::list
更高的空间利用率。
元组tuple
#include <iostream>
#include <tuple>
int main()
{
//构造元组
auto tup = std::make_tuple(21, "zhangsan");
//获得元组某个位置的值
std::cout << "age = " << std::get<0>(tup) << std::endl;
std::cout << "name = " << std::get<1>(tup) << std::endl;
//元组拆包
int age;
std::string name("");
std::tie(age, name) = std::make_tuple(23, "lisi");
std::cout << "age = " << age << std::endl;
std::cout << "name = " << name << std::endl;
return 0;
}
7、智能指针
std::share_ptr
//创建
auto ptr = std::make_shared<int>(10);
std::shared_ptr
可以通过 get()
方法来获取原始指针,通过 reset()
来减少一个引用计数, 并通过use_count()
来查看一个对象的引用计数。
std::unique_ptr
std::unique_ptr
是一种独占的智能指针,它禁止其他智能指针与其共享同一个对象。
//创建
//make_unique 从 C++14 引入
std::unique_ptr<int> ptr = std::make_unique<int>(10);
由于是独占,就是不可复制。但是可以利用 std::move
将其转移给其他的 unique_ptr。
std::weak_ptr
std::weak_ptr
是一种弱引用(相比较而言 std::shared_ptr
就是一种强引用)。
std::weak_ptr
没有 *
运算符和 ->
运算符,所以不能够对资源进行操作,它的唯一作用就是用于检查 std::shared_ptr
是否存在,其 expired()
方法能在资源未被释放时,会返回 false
,否则返回 true
。
多线程
创建线程
#include <iostream>
#include <thread>
int main()
{
std::thread t([]() {
std::cout << "hello world" << std::endl;
});
t.join();
return 0;
}
线程同步
std::mutex
是 C++11 中最基本的 mutex
类,通过实例化 std::mutex
可以创建互斥量, 而通过其成员函数 lock()
可以进行上锁,unlock()
可以进行解锁。 在实际编写代码的过程中,最好不去直接调用成员函数, 因为调用成员函数就需要在每个临界区的出口处调用 unlock()
,当然还包括异常。 这时候 C++11 还为互斥量提供了一个 RAII 语法的模板类 std::lock_guard
。 RAII 在不失代码简洁性的同时,很好的保证了代码的异常安全性。
#include <iostream>
#include <thread>
#include <mutex>
int v = 1;
void critical_section(int change_v)
{
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
// 执行竞争操作
v = change_v;
// 离开此作用域后 mtx 会被释放
}
int main()
{
std::thread t1(critical_section, 2), t2(critical_section, 3);
t1.join();
t2.join();
std::cout << v << std::endl;
return 0;
}
由于 C++ 保证了所有栈对象在生命周期结束时会被销毁,所以这样的代码也是异常安全的。 无论 critical_section()
正常返回、还是在中途抛出异常,都会引发堆栈回退,也就自动调用了 unlock()
。