C++11 & 14 & 17新特性及使用方法

本文使用gcc 8.3.1版本编译。

nullptr:用来替代NULL,区别空指针、0。nullptr的类型为nullptr_t。
#include <iostream>

void foo(char *)
{
    std::cout << "char *" << std::endl;
}

void foo(int)
{
    std::cout << "int" << std::endl;
}

int main(int argc, char const *argv[])
{
    //foo(NULL);
    foo(nullptr);           //nullptr表示空指针,本质还是0,但是具备了类型

    return 0;
}
constptr:显式的生命函数或对象构造函数在编译器会成为常量表达式。
#include <iostream>

#define LEN 10

//时期:
//1.编译器 cpp--编译器--> exe
//2.运行期

//常量表达式
constexpr int len_foo()
{
    return 5;
}

int main(int argc, char const *argv[])
{
    //局部变量,在栈上开辟空间
    char arr_1[10];
    char arr_2[LEN];
    //int len = 5;
    const int len = 5;
    char arr_3[len];            //编译器在运行期才能确定该变量的大小,从而分配空间

    //char arr_3[len + 5];           //非法
    //常量表达式
    char arr_5[len_foo() + 5];     

    return 0;
}
iterator:迭代器,为容器提供统一的遍历方式。
auto:自动类型推导。
#include <iostream>
#include <vector>

int main(int argc, char const *argv[])
{
    //普通的数组:一旦申请,不能再扩增
    int ary[5] = {1, 2, 3, 4, 5};
    //int *pAry = new int[5];

    //容器:动态数组,不用指定其大小,会根据数组当前的使用情况进行动态的扩容
    std::vector<int> v;
    //插入数据
    std::cout << "初始capacity:" << v.capacity() << std::endl;
    for (int i = 0; i < 10; ++i)
    {
        v.push_back(i);
        std::cout << "第 " << i + 1 << " push_back后capacity:" << v.capacity() << std::endl;
    }

    //遍历
    /*
    for (int i = 0; i < v.size(), ++i)
    {
        std::cout << v[i] << " ";
    }
    */

    //使用迭代器的方式遍历
    //迭代器的好处:统一的遍历方式。vector/list/string...
    std::vector<int>::iterator it;          //迭代器,模板类中的内部类
    for (it = v.begin(); it != v.end(); ++it)
    {
        std::cout << *it << " ";
    }    
    std::cout << std::endl;

    //auto类型推导关键字
    for (auto it2 = v.begin(); it2 != v.end(); ++it2)
    {
        std::cout << *it2 << " ";
    }    
    std::cout << std::endl;

    for (auto i : v)
    {
       std::cout << i << " ";
    }    
    std::cout << std::endl;

    return 0;
}
decltype:为了解决 auto 关键字只能对变量进行类型推导的缺陷而出现的,decltype可以用来计算某个表达式的类型。
template <typename T, typename U>
auto add(T x, U y)
{
    return x + y;
}


int main(int argc, char const *argv[])
{
    int x = 1;
    float y = 1.12f;
    auto r = add<int, float>(x, y);
    if (std::is_same<decltype(r), int>::value)
    {
        std::cout << "decltype(x+y) = int" << std::endl;
    }
    else if (std::is_same<decltype(r), float>::value)
    {
        std::cout << "decltype(x+y) = float"  << std::endl;
    }

    return 0;
}
委托构造:Base(int i) : Base,同一个类中一个构造函数调用另一个构造函数(仅能委托一个)。
#include <iostream>

class Base
{
public:
    Base()
    {
        value1 = 1;
    }

    Base(float f)
    {
        value3 = f;
    }

    Base(int i) : Base()            //委托Base()构造函数
    {
        value2 = i;
    }

    Base(int i, float f) : Base(f)
    {
        //委托Base(float)构造函数
        value2 = i;
    }

    //不能委托多个构造函数
    /*Base(int i, float f) : Bae Base(f)
    {
        value2 = i;
    }*/

    int value1;
    int value2;
    float value3;
};

int main(int argc, char const *argv[])
{
    Base b1(2);
    std::cout << b1.value1 << " " << b1.value2 << std::endl;

    Base b2(5, 5.4f);
    std::cout << b2.value1 << " " << b2.value2 << " " << b2.value3 << std::endl;

    return 0;
}
继承构造:using Base::Base; 派生类委托使用基类的构造函数。
    #include <iostream>

class Base
{
public:
    Base()
    {
        value1 = 1;
    }   

    Base(int i) : Base()            //委托Base()构造函数
    {
        value2 = i;
    }

    int value1;
    int value2;
};

//继承构造
class SubClass : public Base
{
public:
    /*SubClass(int v)
    {
        value2 = v;
    }*/

    //直接使用基类的构造
    using Base::Base;
};

int main(int argc, char const *argv[])
{
    SubClass s1(2);
    std::cout << s1.value1 << " " << s1.value2 << std::endl;

    return 0;
}
override:显示声明派生类重写基类的虚函数。
#include <iostream>

class Base
{
public:
    virtual void foo()
    {
        std::cout << "基类foo()" << std::endl;
    }
};

class SubClass : public Base
{
public:
    void foo() override             //override显式说明重写基类的虚函数
    {
        std::cout << "派生类重写基类foo()" << std::endl;
    }
};

int main(int argc, char const *argv[])
{
    SubClass s;
    s.foo();

    return 0;
}
final:防止类被继续继承以及终止虚函数继续重载。final声明的虚函数不能被重写,final声明的类不能被继承。
default:希望编译器产生默认构造。
delete:不让编译器产生默认构造。
#include <iostream>


class Base
{
public:
    Base() = default;           //希望编译器产生默认构造
    //Base() = delete;            //不让编译器产生默认构造
    Base(int n)
    {}

    //virtual void foo() final          //final声明的虚函数不能被重写
    virtual void foo()
    {
        std::cout << "基类foo()" << std::endl;
    }
};

//final声明的类不能被继承
//class SubClass final : public Base 
class SubClass : public Base 
{
public:
    void foo() override             //override显式说明重写基类的虚函数
    {
        std::cout << "派生类重写基类foo()" << std::endl;
    }
};

/*class SubSubClass : public SubClass
{
};*/

int main(int argc, char const *argv[])
{
    SubClass s;
    s.foo();

    Base b;

    return 0;
}
const_cast:移除变量的const或volatile限定符。调用了一个参数不是const的函数,而我们要传进去的实际参数确实const的,但是我们知道这个函数是不会对参数做修改的。
#include <iostream>
#include <string>

class Test
{
public:
    Test() : value(2)
    {}

    //常成员函数
    void foo(int n) const
    {
        //value = n;        //错误
        const_cast<Test* const>(this)->value = n;       //const Test* const this ----> Test* const this
    }

    int value;
};

int main(int argc, char const *argv[])
{
    //const_cast只针对指针,引用,this指针
    const int n = 5;
    //int* p = &n;      //错误
    const int* const_p = &n;
    int* mod_p = const_cast<int*>(const_p);
    *mod_p = 10;
    std::cout << "&n       = " << &n << "     n = " << n << std::endl;
    std::cout << "const_p = " << const_p << "      *const_p = " << *const_p << std::endl;
    std::cout << "mod_p   = " << mod_p << "       *mod_p = " << *mod_p << std::endl;

    Test t;
    std::cout << t.value << std::endl;
    t.foo(10);
    std::cout << t.value << std::endl;

    return 0;
}

输出结果:

&n       = 0x7ffc3de5500c     n = 5
const_p = 0x7ffc3de5500c      *const_p = 10
mod_p   = 0x7ffc3de5500c      *mod_p = 10
2
10
static_cast:基本等价于隐式转换的一种类型转换运算符,可使用于需要明确隐式转换的地方。
#include <iostream>

//基类与派生类之间的转换
class Father
{
public:
    Father()
    {
        value = 5;
    }

    virtual void foo()
    {
        std::cout << "Father::foo()" << std::endl;
    }

    int value;
};

class Son : public Father
{
public:
    virtual void foo() override
    {
        std::cout << "Son::foo()" << std::endl;
    }
};

int main(int argc, char const *argv[])
{
    int n = 5;
    float f = 10.1f;
    
    //f = n;            //本质上,发生了隐式转换

    //static_cast:低风险的转换

    //1.整型与浮点型
    n = static_cast<int>(f);
    std::cout << n << std::endl;

    //2.字符与整型
    char ch = 'a';
    n = static_cast<int>(ch);
    std::cout << n << std::endl;

    //3.void*指针的转换
    void* p = nullptr;
    int* pN = static_cast<int*>(p);

    //4.转换运算符的方式

    //无法转换,高风险的转换:
    
    //1.整型与指针类型转换
    int kk;
    //pN = kk;
    //pN = static_cast<char*>(kk);           //编译不通过

    //基类与派生类之间的转换
    Father* pFather = nullptr;
    Son* pSon = nullptr;
    //基类转派生类(不安全)
    //pSon = pFather;                       //编译不通过
    pSon = static_cast<Son*>(pFather);      //编译不通过。仍然是不安全的,没有提供运行时的检测
    //派生类转基类(安全)
    pFather = pSon;
    pFather = static_cast<Father*>(pSon);

    return 0;
}
dynamic_cast:用于具有虚函数的基类与派生类之间的指针或引用的转换(一般用于向下转换)。
#include <iostream>

//基类与派生类之间的转换
class Father
{
public:
    Father()
    {
        value = 5;
    }

    virtual void foo()
    {
        std::cout << "Father::foo()" << std::endl;
    }

    int value;
};

class Son : public Father
{
public:
    Son()
    {
        value2 = 10;
    }
    virtual void foo() override
    {
        std::cout << "Son::foo()" << std::endl;
    }

    int value2;
};

int main(int argc, char const *argv[])
{
    Father f;
    Son s;

    Father* pFather = &f;
    Son* pSon = &s;

    //向下转换:基类转派生类,不安全
    //用dynamic_cast能够检测出这种转换是不安全的(依赖RTTI)
    //在运行时刻检测转换是否安全
    //有额外的开销,一般而言只有在向下转换时才必须使用
    pSon = dynamic_cast<Son*>(pFather);
    pSon->value2 = 123;

    //向上转换:派生类转基类
    pFather = static_cast<Father*>(pSon);


    return 0;
}
reinterpret_cast:强制转换。
#include <iostream>

//基类与派生类之间的转换
class Father
{
public:
    Father()
    {
        value = 5;
    }

    virtual void foo()
    {
        std::cout << "Father::foo()" << std::endl;
    }

    int value;
};

class Son : public Father
{
public:
    Son()
    {
        value2 = 10;
    }
    virtual void foo() override
    {
        std::cout << "Son::foo()" << std::endl;
    }

    int value2;
};

int main(int argc, char const *argv[])
{
    int n = 1;
    //int* p = (int*)n;

    //用于各种高危险的转换方式
    //1.整型指针
    int* p = reinterpret_cast<int*>(n);

    //2.各种类型的指针转换
    char* pCh = reinterpret_cast<char*>(p);

    //3.基类,派生类指针的转换
    Father f;
    Son s;

    Father* pFather = &f;
    Son* pSon = &s;

    pSon = reinterpret_cast<Son*>(pFather);

    return 0;
}
lambda:匿名函数。
#include <iostream>

int foo(int a, int b)
{
    return a + b;
}

void test01()
{
    //int c = foo(1, 2);
    //lambda表达式就是匿名函数(没有名字的函数)
    //[]捕获列表 ()参数列表 ->返回值
    int c = [](int a, int b) -> int
    {
        //函数主体
        return a + b;
    }(1, 2);

    std::cout << c << std::endl;

    auto f = [](int a, int b) -> int
    {
        return a + b;
    };

    c = f(10, 20);
    std::cout << c << std::endl;
}

//嵌套用法
void test02()
{
    //函数式编程  多线程,并发
    int c = [](int n)
    {
        return [n](int x)
        {
            return n + x;
        }(1);
    }(2);

    std::cout << c << std::endl;

    auto f = [](int n)
    {
        return [n](int x)
        {
            return n + x;
        };
    };

    c = f(10)(20);
    std::cout << c << std::endl;
}

int main(int argc, char const *argv[])
{
    //test01();
    test02();

    return 0;
}

lambda中mutable用法:

#include <iostream>

int main(int argc, char const *argv[])
{
    int t = 10;
    //Lambda 表达式内部函数体在默认情况下是不能够使用函数体外部的变量的, 这时候捕获列表可以起到传递外部数据的作用
    //按值捕捉
    auto f = [t]() mutable
    {
        return ++t;
    };

    auto f2 = [t]() mutable
    {
        return ++t;
    };

    std::cout << f() << std::endl;      //11
    std::cout << f2() << std::endl;      //11
    std::cout << f() << std::endl;      //12
    std::cout << f2() << std::endl;      //12
    std::cout << f() << std::endl;      //13
    std::cout << f2() << std::endl;      //13
    std::cout << t << std::endl;        //10

    return 0;
}

lambda捕获方式:

#include <iostream>
#include <vector>
#include <algorithm>

void test01()
{
    int t = 10;
    //1.按值捕获,捕获的是声明匿名函数时,捕获列表参数的值
    auto f = [t]()
    {
        std::cout << t << std::endl;            //10
    };

    t = 11;
    f();
}

void test02()
{
    //2.按引用捕获
    int t = 10;
    auto f2 = [&t]()
    {
        std::cout << t << std::endl;            //12
        t = 13;
    };

    t = 12;
    f2();
    std::cout << t << std::endl;                //13
}

void test03()
{
    //隐式捕获
    int a = 1;
    int b = 2;
    int c = 3;
    int d = 4;
    //按值捕获所有的变量
    [=]()
    {
        std::cout << a << " + " << b << std::endl;
    }();

    //按引用捕获所有的变量
    [&]()
    {
        std::cout << ++a << " + " << ++b << std::endl;
    }();

    //按照值和引用的方式捕获变量
    [a, b, &c, &d]()
    {
        std::cout << a << " + " << b << std::endl;
        std::cout << ++c << " + " << ++d << std::endl;
    }();
}

//应用
void test04()
{
    std::vector<int> v = {1, 2, 3, 4, 5};
 /*   for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it)
    {
        if (*it % 2 == 0)
        {
            std::cout << *it << "是偶数" << std::endl;
        }
        else
        {
            std::cout << *it << "是奇数" << std::endl;
        }
    }*/

    //高阶写法
    for_each(v.begin(), v.end(), [&](int n)
    {
        if (n % 2 == 0)
        {
            std::cout << n << "是偶数" << std::endl;
        }
        else
        {
            std::cout << n << "是奇数" << std::endl;
        }
    });
}

int main(int argc, char const *argv[])
{
    //test01();
    //test02();
    //test03();
    test04();

    return 0;
}
function:一种通用、多态的函数封装, 它的实例可以对任何可以调用的目标实体进行存储、复制和调用操作, 它也是对 C++ 中现有的可调用实体的一种类型安全的包裹(相对来说,函数指针的调用不是类型安全的), 换句话说,就是函数的容器。当我们有了函数的容器之后便能够更加方便的将函数、函数指针作为对象进行处理
bind:用来绑定函数调用的参数的。
#include <iostream>
#include <functional>

class Test
{
public:
    Test() = default;

    int MyTest(int n)
    {
        std::cout << n << std::endl;
        return n;
    }

    int operator()(int n)
    {
        std::cout << n << std::endl;
        return n;
    }
};

int test(int n)
{
    std::cout << n << std::endl;
    return n;
}

void print(int a, int b, int c)
{
    std::cout << a << " " << b << " " << c << std::endl;
}

int main(int argc, char const *argv[])
{
    //函数对象包装器
    //为了函数提供了一种容器(封装)
    //test(1);

    //支持4中函数的封装
    //1.普通函数
    std::function<int(int)> f = test;
    f(123);

    //2.匿名函数
    std::function<int(int)> f2 = [](int n) -> int
    {
        std::cout << n << std::endl;
        return n;
    };
    f2(456);

    //3.类的成员函数
    std::function<int(Test*, int)> f3 = &Test::MyTest;
    Test t;
    f3(&t,789);

    //4.仿函数(重载了()运算符的函数)
    t(111);
    std::function<int(Test*, int)> f4 = &Test::operator();
    f4(&t, 222);

    //bind机制
    auto a = std::bind(print, 1, 2, 3);
    a();

    auto b = std::bind(print, std::placeholders::_1, std::placeholders::_2, 3);
    b(100, 200);

    return 0;
}
T &&:右值引用的声明让这个临时值的生命周期得以延长、只要变量还活着,那么将亡值将继续存活。
std::move:将左值参数无条件的转换为右值。
#include <iostream>

void reference(std::string& str)
{
    std::cout << "左值" << std::endl;
}

void reference(std::string&& str)
{
    std::cout << "右值" << std::endl;
}

int main(int argc, char const *argv[])
{
    std::string lv1 = "string";         //lv1是一个左值
    //std::string&& r1 = lv1;             //非法,右值引用不能引用左值
    std::string&& rv1 = std::move(lv1);        //合法,std::move可以将左值转换为右值
    std::cout << rv1 << std::endl;
    const std::string& lv2 = lv1 + lv1;         //合法,常量左值引用能够延长临时变量的生命周期
    //lv2 += "Tese";                          //非法,常量引用无法修改
    std::cout << lv2 << std::endl;

    std::string&& rv2 = lv1 + lv2;           //合法,右值引用延长临时对象生命周期
    rv2 += "Test";                      //合法,非常量引用能够修改临时变量
    std::cout << rv2 << std::endl;

    reference(rv2);                     //输出左值。rv2虽然引用了一个右值,但由于它是一个引用,所以rv2依然是一个左值。


    // int &a = std::move(1);    // 不合法,非常量左引用无法引用右值
    const int &b = std::move(1); // 合法, 常量左引用允许引用右值

    return 0;
}
移动语义:传统的 C++ 没有区分『移动』和『拷贝』的概念,造成了大量的数据拷贝,浪费时间和空间。
#include <iostream>

class A
{
public:
    //默认构造
    A() : pointer(new int(1))
    {
        std::cout << "默认构造" << pointer << std::endl;
    }

    //拷贝构造
    A(A& a) : pointer(new int(*a.pointer))
    {
        std::cout << "拷贝构造" << pointer << std::endl;
    } 

    //移动构造:避免先复制,再析构,浪费时间和空间
    A(A&& a) : pointer(a.pointer)
    {
        a.pointer == nullptr;
        std::cout << "移动构造" << pointer << std::endl;
    }

    int* pointer;
};

//防止编译器优化
A return_rvalue(bool test)
{
    A a,b;
    if (test)
        return a;           //等价于static_cast<A&&>(a);
    else
        return b;           //等价于static_cast<A&&>(b);
}

int main(int argc, char const *argv[])
{
/*函数返回后,产生一个将亡值,被 A 的移动构造(A(A&&))引用,从而延长生命周期,并将这个右值中的指针拿到,保存到了 obj 中,而将亡值的指针被设置为 nullptr,防止了这块内存区域被销毁。*/
    A obj = return_rvalue(false);
    std::cout << "obj.pointer: " << obj.pointer << " *obj.pointer: " << *obj.pointer << std::endl;

    return 0;
}
std::array:容器大小固定的线性容器(std::vector是自动扩容)。
#include <iostream>
#include <vector>
#include <array>
#include <algorithm>

void test01()
{
    std::vector<int> v;
    std::cout << "size:" << v.size() << std::endl;         // 输出 0
    std::cout << "capacity:" << v.capacity() << std::endl; // 输出 0

    // 如下可看出 std::vector 的存储是自动管理的,按需自动扩张
    // 但是如果空间不足,需要重新分配更多内存,而重分配内存通常是性能上有开销的操作
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    std::cout << "size:" << v.size() << std::endl;         // 输出 3
    std::cout << "capacity:" << v.capacity() << std::endl; // 输出 4

    // 这里的自动扩张逻辑与 Golang 的 slice 很像
    v.push_back(4);
    v.push_back(5);
    std::cout << "size:" << v.size() << std::endl;         // 输出 5
    std::cout << "capacity:" << v.capacity() << std::endl; // 输出 8

    //如下可以看出vector虽然清空了容器,但是被清空元素的内存并没有归还
    v.clear();
    std::cout << "size:" << v.size() << std::endl;         // 输出 0
    std::cout << "capacity:" << v.capacity() << std::endl; // 输出 8

    //额外内存可通过skrink_to_fit()调用返会给系统
    v.shrink_to_fit();
    std::cout << "size:" << v.size() << std::endl;         // 输出 0
    std::cout << "capacity:" << v.capacity() << std::endl; // 输出 0
}

void test02()
{
    //数组大小必须是常量表达式
    //int len = 4;                  //非法
    constexpr int len = 4;
    std::array<int, len> arr = {1, 2, 3, 4};
    if (!arr.empty())
    {
        std::cout << "size:" << arr.size() << std::endl;
    }

    //用lambda表达式排序
    std::sort(arr.begin(), arr.end(), [](int a, int b) {
        return b < a;
    });

    for (auto& i : arr)
    {
        std::cout << i << " ";
    }
    std::cout << std::endl;
}

int main(int argc, char const *argv[])
{
    //test01();
    test02();

    return 0;
}
std::forward_list:使用方法和 std::list 基本类似。需要知道的是,和 std::list 的双向链表的实现不同,std::forward_list 使用单向链表进行实现, 提供了 O(1) 复杂度的元素插入,不支持快速随机访问(这也是链表的特点), 也是标准库容器中唯一一个不提供 size() 方法的容器。当不需要双向迭代时,具有比 std::list 更高的空间利用率。
元组:来存放不同类型的数据。std::make_tuple构造元组;std::get获取元组某个位置的值;std::tie元组拆包;std::tuple_cat元组合并。
#include <iostream>
#include <tuple>

auto get_student(int id)
{   
    //返回类型被推断为std::tuple<double, char, std::string>
    if (id == 0)
        return std::make_tuple(3.8, 'A', "张三");
    else if (id == 1)
        return std::make_tuple(2.9, 'B', "李四");
    else if (id == 2)
        return std::make_tuple(1.7, 'C', "王五");
    return std::make_tuple(0.0, 'D', "null");
    // 如果只写 0 会出现推断错误, 编译失败
}

void test01()
{
    std::tuple<double, int, std::string> student = get_student(0);
    std::cout << "ID: 0, GPA: " << std::get<0>(student) << ",成绩: " \
              << std::get<1>(student) << ",姓名: " << std::get<2>(student) << std::endl;

    //元组进行拆包
    double gpa;
    char grade;
    std::string name;
    std::tie(gpa, grade, name) = get_student(1);
    std::cout << "ID: 0, GPA: " << gpa << ",成绩: " << grade << ",姓名: " << name << std::endl;
}

void test02()
{
    //added by c++14
    std::tuple<std::string, double, double, int> t = std::make_tuple("ABC", 3.14, 2.15, 8);
    std::cout << std::get<std::string>(t) << std::endl;
    //std::cout << std::get<double>(t) << std::endl;            //非法
    std::cout << std::get<int>(t) << std::endl;

    auto new_tuple = std::tuple_cat(get_student(1), t);
}

int main(int argc, char const *argv[])
{
    test01();
    test02();

    return 0;
}
std::unique_ptr:独占的智能指针,不可复制。
#include <iostream>
#include <memory>
#include <vector>

using std::cout;
using std::endl;

void foo_constuct()
{
    //这样构造是可以的
    std::unique_ptr<int> p(new int(3));

    //空构造
    std::unique_ptr<int> p4;

    //下面三种写法会报错
    // std::unique_ptr<int> p2 = p;         //需要拷贝构造
    // std::unique_ptr<int> p3(p);          //需要拷贝构造
    // p4 = p;                              //需要=运算符重载
}

void foo_reset()
{
    //释放
    int *pNew = new int(3);
    int *p = new int(5);
    {
        std::unique_ptr<int> uptr(pNew);
        uptr.reset(p);
    }
}

void foo_move()
{
    std::unique_ptr<int> uptr = std::make_unique<int>(4);       //C++14添加
    std::unique_ptr<int> uptr2 = std::move(uptr);               //剪切操作
    //cout << *uptr << endl;                                    //非法,std::unique_ptr是独占的智能指针,不可复制

    std::unique_ptr<int> uptr3;
    uptr3 = std::move(uptr2);
}

void foo_ary()
{
    std::vector<std::unique_ptr<int>> Vec;
    std::unique_ptr<int> uptr = std::make_unique<int>(3);
    //Vec.push_back(uptr);                                      //非法,需要使用std::move操作
    Vec.push_back(std::move(uptr));

    //cout << *uptr << endl;                                    //非法,std::unique_ptr是独占的智能指针,不可复制
}

int main(int argc, char const *argv[])
{
    foo_constuct();
    foo_move();
    foo_ary();

    return 0;
}
std::shared_ptr:带引用计数的智能指针。std::make_shared();sptr.use_count()。
#include <iostream>
#include <memory>

using std::cout;
using std::endl;

void foo_constuct()
{
    std::shared_ptr<int> sptr(new int(4));
    std::shared_ptr<int> sptr2(sptr);
    std::shared_ptr<int> sptr3 = sptr2;
    std::shared_ptr<int> sptr4 = std::make_shared<int>(5);
}

void foo(std::shared_ptr<int> p)
{
    (*p)++;
}

void test01()
{
    std::shared_ptr<int> sptr1 = std::make_shared<int>(10);
    foo(sptr1);
    cout << *sptr1 << endl;         //11

    auto sptr2 = sptr1;             //引用加1
    auto sptr3 = sptr1;             //引用加1
    int *p = sptr1.get();           //这样不会增加引用计数

    cout << "sptr1.use_count: " << sptr1.use_count() << endl;
    cout << "sptr2.use_count: " << sptr2.use_count() << endl;
    cout << "sptr3.use_count: " << sptr3.use_count() << endl;
}

void test02()
{
    //如果用同一个指针去初始化两个shared_ptr时,则引用计数仍然会出错
    int *p = new int(3);
    {
        //重复释放
        std::shared_ptr<int> sptr(p);
        {
            std::shared_ptr<int> sptr2(p);
        }
    }
}

int main(int argc, char const *argv[])
{
    foo_constuct();
    test01();
    //test02();

    return 0;
}

weak_ptr:弱指针。std::weak_ptr 没有* 运算符和-> 运算符,所以不能够对资源进行操作,它的唯一作用就是用于检查std::shared_ptr 是否存在。因此,实际上在使用弱指针时,不能通过弱指针,直接访问内部指针的数据,而应该是先判断该弱指针所观察的强指针是否存在(调用expired()函数), 如果存在,那么则使用lock()函数来获取一个新的shared_ptr来使用对应的内部指针。
#include <iostream>
#include <memory>

using std::cout;
using std::endl;

class Son;
class Parent;

class Son
{
public:
    Son()
    {}

    ~Son()
    {}

    void Set(std::shared_ptr<Parent> parent)
    {
        m_pParent = parent;
    }

    std::shared_ptr<Parent> m_pParent;
};

class Parent
{
public:
    Parent()
    {}

    ~Parent()
    {}

    void Set(std::shared_ptr<Son> pSon)
    {
        m_pSon = pSon;
    }

    std::shared_ptr<Son> m_pSon;
};

void testShared()
{
    Son *pSon = new Son();
    Parent *pParent = new Parent();
    {
        std::shared_ptr<Parent> shared_Parent(pParent);
        std::shared_ptr<Son> shared_Son(pSon);

        shared_Parent->Set(shared_Son);
        shared_Son->Set(shared_Parent);

        cout << "shared_Parent.use_count: " << shared_Parent.use_count() << endl;           //shared_Parent.use_count: 2
        cout << "shared_Son.use_count: " << shared_Son.use_count() << endl;                 //shared_Son.use_count: 2
    }
    //通过gdb调试,在这个位置可以看到shared_Parent.use_count和shared_Son.use_count均为1,两个对象均未被销毁
    /*最后两者的引用计数均为1,原因是出了块作用域之后,两个shared_parent和shared_son均会析构,
    在这两个智能指针的内部,均会先去判断对应的内部指针是否-1是否为0,显然这里相互引用的情况下,
    引用计数初值为2,减1后值为1,所以两个指针均不会被释放。这里,其实只需要一个释放了,
    另外一个也能跟着释放,可以采用弱指针,即人为的迫使其中一个引用计数为1,从而打破闭环。
    这里只需要将上例子中的任意一个强指针改为弱指针即可。
    原因是,弱指针的引用不会增加原来的引用计数,那么就使得引用不再是闭环,所以在出作用域之后,全部得到释放。*/
}

void foo_weak()
{
    /*std::weak_ptr 没有* 运算符和-> 运算符,所以不能够对资源进行操作,它的唯一作用就是
    用于检查std::shared_ptr 是否存在。*/
    /*因此,实际上在使用弱指针时,不能通过弱指针,直接访问内部指针的数据,
    而应该是先判断该弱指针所观察的强指针是否存在(调用expired()函数),
    如果存在,那么则使用lock()函数来获取一个新的shared_ptr来使用对应的内部指针*/
    auto sptr = std::make_shared<int>(3);
    auto sptr2 = sptr;
    std::weak_ptr<int> wptr = sptr;
    cout << "sptr.use_count: " << sptr.use_count() << endl;         //ptr.use_count: 2
    if (!wptr.expired())
    {
        std::shared_ptr<int> ptr3 = wptr.lock();
        cout << "sptr.use_count: " << sptr.use_count() << endl;     //ptr.use_count: 3
    }
}

int main(int argc, char const *argv[])
{

    testShared();
    foo_weak();

    return 0;
}

thread和mutex

thread和mutex参照这篇博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值