文章目录
1.std::thread
- 可以看一下
/usr/include/c++/4.8/thread
里面有完整的thread类声明 - std::thread创建一个新的线程可以接受任意的可调用对象类型(带参数或不带参数),包括lambda表达式(带变量捕获或不带),函数,函数对象,以及函数指针。
-
- 传入函数指针和参数 线程立即执行 2.空构造 不会新增线程
std::thread t(fun); //fun为某个函数
t.join();
-
通过
std::this_thread::get_id()
获取线程的标识符也可以直接
t.get_id()
-
std::thread::id可以进行比较,可以作为关联容器的key (std::thread::id重载了operator==允许我们比较两个线程是否相等)
注意 std::thread的等于操作符必须要通过move语义,并且如果joinable的话,会执行std::terminate()
https://www.ibm.com/developerworks/cn/linux/1412_zhupx_thread/
2.std::bind
绑定函数与参数,返回一个函数对象
auto fn_1 = std::bind(my_divide,10,2); //返回10/2
std::cout<<fn_1()<<endl;
auto fn_2 = std::bind<int>(my_divide,_1,_2) //返回int(x/y)
std::cout<<fn_2(10,3)<<endl;
std::bind 将可调用对象与参数一起进行绑定,绑定后的结果可以使用std::function保存。
将可调用对象和其参数绑定成一个函数
只绑定部分参数,减少可调用对象传入的参数
std::bind和std::function一起使用 绑定成员函数,绑定成员变量 见6.std::function
3.std::ref
std::bind在使用时,是对参数直接拷贝,而不是引用,不对参数直接操作。
我们希望传递给bind一个对象而不拷贝他,就必须使用标准库ref函数。
for_each(words.begin(), words.end(), std::bind(print, std::ref(os), _1, ' '))
(// 这个using是为了使用 _1, _2, _3,…占位符usingnamespace std::placeholders;
)
函数ref返回一个对象,包含给定的引用,此对象是可以拷贝的.
⤵️std::cref生成一个保存const引用的类。
#include <functional>
4.std::move
返回一个右值引用到ARG,强制移动语意值的辅助函数
static_cast<remove_reference<decltype(arg)>::type&&>(arg)
可以将左值引用转换为右值引用
避免不必要的拷贝,性能提升
将对象的状态或所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝
std::string a = "hello";
std::string b = "world";
std::cout<<"a "<<a<<std::endl;
std::cout<<"b "<<b<<std::endl;
a = std::move(b);
std::cout<<"a "<<a<<std::endl;
std::cout<<"b "<<b<<std::endl;
输出
a hello
b world
a world
b
可以看出直接将b的所有给a,b中不再有东西,是直接把b指向地址的所有权给a,b不再指向那一块地址
5.std::vector
对于vector的遍历有一个好的栗子
std::vector<std::string> myvector;
std::string foo = "foo-string";
myvector.push_back(foo);
for(std::string& x:myvector) std::cout<<' '<<x; //这句引用用的很好
std::cout<<'\n';
添加
obj.push_back(foo);
擦除
obj.erase(obj.begin() + 4);
obj.front() 和 obj.begin() 的区别
front是返回引用,begin返回迭代器
同理于back 和 end
一般排序时用迭代器指示
sort(obj.begin(), obj.end());
reverse(obj.begin(), obj.end());//逆序
sort(obj.rbegin(), obj.rend());//逆序排序
- capacity()
返回当前分配存储的容量
6.std::function
是可调用对象的包装器,最重要的功能是实现延时调用
可以理解为函数指针
- 保存自由函数
void printA(int a)
{
cout<<a<<endl;
}
std::function<void(int a)> func;
func = printA;
func(2);
-
保存lambda表达式
本质上是匿名函数对象
std::function<void()>func_1 = [](){cout<<"hello world"<<endl;}
func_1();
-
std::bind将可调用对象与其参数一起绑定。绑定后可以使用std::function进行保存并延迟到我们需要的时候使用
- 将可调用对象与其参数绑定成一个仿函数
- 可绑定部分参数
在绑定部分参数时,通过使用std::placeholders来决定空位参数将会是调用发生时的第几个参数
#include <functional> class A { public: int i_ = 0; //c11允许非静态数据成员在其声明处进行初始化 void output(int x, int y) { std::cout<<x<<" "<<y<<std::endl; } private: int j_; }; int main() { A a; //绑定成员函数,保存为仿函数 std::function<void(int, int)>fun1 = std::bind(&A::output, &a, std::placeholders::_1, std::placeholders::_2); //调用成员函数 fun1(1,2); //绑定成员变量 std::function<int&(void)>fun2 = std::bind(&A::i_, &a); //std::function<int&(void)>fun2 = std::bind(&A::j_, &a); //不能绑定私有成员变量 fun2() = 100; fun2() = 100; std::cout<<a.j_<<std::endl; }
7.lambda表达式
本质上来讲:匿名函数对象
优点:
-
同样是函数对象,但是它立即可见
-
它是匿名对象,用完出块立即释放,不会污染命名空间
-
相比于std::bind,lambda expr有效率上的提升
-
减少心智负担(回调机制使得代码支零破碎)
8.原子操作
头文件 #include <atomic>
基本实现 std::atomic<T>
拓展实现 std::atomic_char,std::atomiic_int,std::atomic_uint
是为了实现多线程共享资源不被乱序访问,而使单步操作变得很短很短【小到不可分割】,从而实现原子操作
你可以像使用内置的数据结构那样使用原子数据类型(c++保证这些操作是原子操作)对应内置的数据类型,原子数据都有一份对应的类型。 更多的请见:http://en.cppreference.com/w/cpp/atomic/atomic
std::atomic_long sum = {0L};
std::atomic_long sum = {0L}; //long sum = 0L;
//此处两种不同变量的定义 结果不同 将sum定义为原子数据类型后为原子操作
void fun()
{
for(int i=0;i<100000;++i)
sum += i;
}
int main()
{
std::cout << "Before joining,sun = " << sum << std::endl;
std::thread t1(fun);
std::thread t2(fun);
t1.join();
t2.join();
std::cout << "After joining,sun = " << sum << std::endl;
}
结果分别是
Before joining,sun = 0
After joining,sun = 9999900000
和
Before joining,sun = 0
After joining,sun = 7928483429
9.Container::emplace
减少拷贝,提高效率
10、右值引用、noexcept、 =delete 可调用对象
=delete
thread(const thread&) = delete;
表示禁用拷贝构造
-
noexcept
该关键字告诉编译器,函数中不会发生异常,有利于编译器对程序做更多优化
如果在运行时,noexecpt函数向外抛出了异常,程序会直接终止,调用std::terminate()函数,该函数内部会调用std::abort()终止程序。
他在一定程度上取代了throw()
constexpr initializer_list() noexcept : _M_array(0), _M_len(0) { }
void swap(Type& x, Type& y) throw() //C++11之前 { x.swap(y); } void swap(Type& x, Type& y) noexcept //C++11 { x.swap(y); }//单独使用noexcept,表示其所限定的swap函数绝对不发生异常
使用方法也可以更灵活
void swap(Type&x, Type& y) noexcept(noexcept(x.swap(y))) //c++11 { x.swap(y); }//表示 如果操作x.swap(y)不发生异常,那么函数swap(Type& x, Type& y)一定不发生异常
在移动分配函数(move assignment)的应用
pair& operator=(pair&& __p)
noexcept(__and_<is_nothrow_move_assignable<_T1>,
is_nothrow_move_assignable<_T2>>::value)
{
first = std::forward<first_type>(__p.first);
second = std::forward<second_type>(__p.second);
return *this;
}
//表明 如果类型T1和T2的移动分配过程中不发生异常,那么该移动构造函数就不会发生异常
什么时候建议使用(其他时候不建议使用!):
- 移动构造函数(move constructor)
- 移动分配函数(move assignment)
- 析构函数(destructor)
- 叶子函数(Leaf Function)。叶子函数是指在函数内部不分配栈空间,也不调用其它函数,也不存储非易失性寄存器,也不处理异常。
11.智能指针
shared_ptr允许多个指针指向同一个对象,unique_ptr独占所指的对象,weak_ptr是一种弱引用,指向shared_ptr所管理的对象
- shared_ptr和unique_ptr都支持的操作
shared_ptr<T> p; //空智能指针,可以指向类型为T的对象
unique_ptr<T> p;
p; //将p作为一个条件判断,若p指向一个对象,则为true
*p; //解引用p获得它指向的对象
p->get(); //返回p中保存的指针。要小心使用,若智能指针释放了其对象返回的指针,所指向的对象也就消失了
swap(p,q); //交换p、q中的指针
p.swap(q);
- shared_ptr其他方法
shared_ptr<T> p(q) //p管理内置指针q所指的对象; q必须指向new分配的内存,且能够转换为T*类型
shared_ptr<T> p(u) //p从unique_ptr u 那里接管了对象的所有权;将u置为空
shared_ptr<T> p(q,d) //p接管了内置指针q所指向的对象的所有权;q必须能转换为T*类型。p将使用可调用的对象d来代替delete
shared_ptr<T> p(p2,d) //p是shared_ptr p2的拷贝,唯一的区别是p将调用d来代替delete
p.reset(); //若p是唯一指向其对象的shared_ptr,reset会释放此对象。
p.reset(q); //若传递了可选参数内置指针q,会令p指向q,否则会将p为空
p.reset(q.d); //若还传递了参数d,将会调用d而不是delete来释放q
- 初始化:
//一般的初始化方式
shared_ptr<string> print(new string("normal usage!"));
cout<<*print<<endl;
//推荐的安全的初始化方式
shared_ptr<string> print1 = make_shared<string>("safe usage!");
cout<<*print1<<endl;
- get()函数
返回一个内置指针,指向智能指针管理的对象。
设置的初衷是当我们向不能使用智能指针的代码传递一个内置指针。
使用get返回指针的代码不能delete此指针。
栗子:
shared_ptr<int> p1 = make_shared<int>(32);
cout<<*(p.get())<<endl;
shared_ptr<string> s = make_shared<string>("hhhhhha");
//可以直接写成 auto s = make_shared<string>("hhhhhha");
cout<<*(s.get())<<endl;
//错误的操作
shared_ptr<int>p2(p1.get()); //p1、p2各自保留了对一段内存的引用计数,其中有一个引用计数耗尽,资源也就释放了
delete (p1.get)//error!
- shared_ptr的拷贝和赋值 引用计数器 use_count();
auto p = make_shared<int>(42); //p指向的对象只有p一个引用者
cout<<p.use_count()<<endl; //1
auto q(p); //p和q指向相同的对象,此对象有两个引用者
cout<<p.use_count()<<endl; //2
auto r = make_shared<int>(56); //指向的对象只有r一个引用者
cout<<r.use_count()<<endl; //1
r = p; //r原来引用的对象经过赋值之后释放掉了,p引用的对象有p、q、r三个引用
cout<<*p<<"=="<<*q<<"=="<<*r; //42==42==42
count<<p.use_count()<<endl; //3
count<<q.use_count()<<endl; //3
count<<r.use_count()<<endl; //3
r = p; //r原来引用的对象经过赋值之后释放掉了,直接指向新引用
- 容器中的shared_ptr记得用erease节省内存
对于一块内存,shared_ptr保证只要有任何shared_ptr对象引用它,他就不会被释放掉。
–由于这个特性,保证shared_ptr在不用之后不再保留就非常中,通常这个过程能自动执行,不需要人工干预,但是有一种例外是我们将shared_ptr放在了容器中。所以千万不要忘记erease不用的shared_pte
- std::enable_shared_from_this
能让其中一个对象(假设其名为t,且已被std::shared_ptr对象pt管理)安全地生成其他额外的std::shared_ptr实例(假设名为pt1,pt2,…)他们与pt共享对象t的所有权
若一个类T继承std::enable_shared_from_this<T>
,则会为该类T提供成员函数:shared_from_this
当T类型对象t被一个命名为pt的std::shared_ptr<T>
类对象管理时,
调用T::shared_from_this
成员函数,将会返回一个新的std::shared_ptr<T>
对象,它将与pt共享t的所有权。
(未完待续)
不足的以后持续补充中