c++11笔记

文章目录

      • 1.std::thread
      • 2.std::bind
      • 3.std::ref
      • 4.std::move
      • 5.std::vector
      • 6.std::function
      • 7.lambda表达式
      • 8.原子操作
      • 9.Container::emplace
      • 10、右值引用、noexcept、 =delete 可调用对象
      • 11.智能指针

1.std::thread

  • 可以看一下 /usr/include/c++/4.8/thread 里面有完整的thread类声明
  • std::thread创建一个新的线程可以接受任意的可调用对象类型(带参数或不带参数),包括lambda表达式(带变量捕获或不带),函数,函数对象,以及函数指针。
    1. 传入函数指针和参数 线程立即执行 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

4.std::move

返回一个右值引用到ARG,强制移动语意值的辅助函数

static_cast::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::functionfunc_1 = [](){cout<<"hello world"<
  • std::bind将可调用对象与其参数一起绑定。绑定后可以使用std::function进行保存并延迟到我们需要的时候使用

    • 将可调用对象与其参数绑定成一个仿函数
    • 可绑定部分参数

    在绑定部分参数时,通过使用std::placeholders来决定空位参数将会是调用发生时的第几个参数

    #include 
    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::functionfun2 = std::bind(&A::j_, &a); //不能绑定私有成员变量
        fun2() = 100;
        fun2() = 100;
        std::cout<<a.j_<<std::endl;
    
    }   
    

7.lambda表达式

本质上来讲:匿名函数对象

优点:

  • 同样是函数对象,但是它立即可见

  • 它是匿名对象,用完出块立即释放,不会污染命名空间

  • 相比于std::bind,lambda expr有效率上的提升

  • 减少心智负担(回调机制使得代码支零破碎)

8.原子操作

头文件 #include
基本实现 std::atomic
拓展实现 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("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提供成员函数:shared_from_this

当T类型对象t被一个命名为pt的std::shared_ptr类对象管理时,

调用T::shared_from_this成员函数,将会返回一个新的std::shared_ptr对象,它将与pt共享t的所有权。

(未完待续)
不足的以后持续补充中

你可能感兴趣的:(c++)