c++11 新特性总结

文章内部代码地址:https://gitee.com/zhr2000/c-new-features

auto & decltype

这两个关键字在编译期就推到出变量或者表达式的类型

  • auto:可以通过=右边的类型推导出变量的类型(自动推导)
  • decltype:用于推导表达式类型,只用于编译器分析表达式的类型,表达式实际不会进行运算。主要用在模板和泛型编程中(反推导)。
  • auto和decltype的区别:auto是通过初始化表达式推导出类型,而decltype是通过普通表达式的返回值推导出类型。
  • RTTI机制:为每个类型产生一个type_info类型数据,程序员可以在程序中使用typeid随时查询一个变量的类型,typeid就会返回变量相应的type_info数据,type_info的name成员可以返回类型的名字。在C++11中,增加了hash_code这个成员函数,返回该类型唯一的哈希值,以供程序员对变量类型随时进行比较。(完整的解释),RTTI只能识别出类型但是不能使用。
  • 追踪返回值类型:函数返回值无法确定的时候
//追踪返回值类型(主要是用在泛型编程)
template<typename t1,typename t2>
auto function(const t1 x,const t2 y) ->decltype(x+y){
    return x+y;
}
初始化
  • 类内成员初始化:
// 参数列表初始化
class people{
    public:
    people(int x,string y) : age(x),name(y){ }; 
    int age;
    string name;
};


//类中成员变量可以直接初始化
class people2{
    public:
    //直接用 =
    int age = 10;
    //用 { }
    string name {"hello"};
    people son {5,"world"};
};
  • 列表初始化可以防止类型收窄 (但是有些编译器是可以编过但是会有警告)
 // 列表初始化防止类型收窄
    int a = 1024;
    char b = a;   //会造成数据丢失
    cout << b << endl; //不会报错

    int c = 1024;
    char d {c};    //会报警告
    cout << d <<endl;
基于范围的for循环
  • 基于范围的for循环 传递的是值,不是传递的地址
    //基于范围的for循环传递的是值,不是传递的地址
    int a[] {1,2,3,4,5,6,7};
    for(auto temp : a){
        cout << temp <<endl;
    }

    for(auto& temp :a){
        temp = temp*2;
        cout << temp <<endl;
    }
静态断言
  • static_assert :与传统断言的区别是程序还没运行的时候就能检查条件是否满足,因为是静态的。
    下面代码是传统断言(运行时才能检查)和静态断言的区别。
    //传统的断言(c语言的头文件):
    //运行时检查条件,如果条件为真,往下执行,如果条件为假,中断并提示错误
    auto flag = true;
    assert(flag == true);
    cout << "hello assert" << endl;

    //c++11的静态断言,
    //static_assert (常量表达式, “提示的字符串”)

    static_assert(sizeof(void*)==8,"64位系统不支持");
noexcept 修饰符

noexcept 除了声明函数不抛出异常,还是一个运算符,它能够用来说明函数是否会抛出异常,
例如在我们希望只有在T是一个基础类型时复制函数才会被声明为noexcept,因为基础类型的复制是不会发生异常的。这时就需要用到带参数的noexcept了:

template <class T> 
T copy(const T &o) noexcept(std::is_fundamental<T>::value) {
	// ...
}

这段代码通过std::is_fundamental 来判断T是否为基础类型,如果T是基础类型,则复制函数被声明noexcept(true)
即不会抛出异常。反之,函数被声明为noexcept(false),表示函数有可能抛出异常。由于noexcept对表达式的评估是在编译阶段执行的,因此表达式必须是一个常量表达式

void function()throw(){ }
void function2()noexcept;   //不抛出异常
void function3()noexcept(1==1){ }; //表达式为真抛出异常,表达式为假不抛出异常
nullptr
  • 解决了之前NULL 和 0相同的问题(二义性),nullptr只能赋值给指针变量。
强类型枚举
  • c++11之前是不能定义两个相同的枚举的
    //以前的枚举是不能定义两个相同的
    enum status{ok,err};
    enum status2{ok,err}; //会报错
  • C++11 强类型枚举:enum后面加 class 或者 struct 关键字,在使用的时候要加作用域,强类型枚举还可以指定成员的类型(必须为基础数据类型)
    //强类型枚举,enum后面加 class 或者 struct 关键字,在使用的时候要加作用域
    enum class S {ok,err};
    enum struct S2 {ok ,err};
    S flag =  S :: ok ;
    S2 flag2 = S2 :: ok;
	enum class S3 : char {ok.err};
常量表达式 constexpr
  • 允许一些计算发生在编译时,只能有 函数体中只能有一个 return 语句,但是允许包含typedef,using ,静态断言。
  • 类中使用constexpr进行构造
class date{
    public:
    //里面必须式基础数据类型
    constexpr date(int x,int y) : age(x),male(y){

    }

    constexpr int getage(){
        return this->age;
    }
    constexpr int getmale(){
        return this->male;
    }

    private:
    int age;
    int male;
};
自定义字面量
  • 定义字面量(自定义后缀),名字要求 operate""xxx()
  • 只有这些签名是合法的,后面四个中,第二个参数会自动推导字符串的长度,注意第一个不是字符串,而是叫做原始字面量,就是说传入的是啥就是啥
char const*
unsigned long long
long double
char const*, std::size_t
wchar_t const*, std::size_t
char16_t const*, std::size_t
char32_t const*, std::size_t


//注意事项
char const* operator"" _r(char const* s)
{
    return s;
}

int main()
{
    std::cout << 12_r << '\n';
    //输出值是12
}


字面量的返回值并没有被严格限定。我们完全可以提供相容类型的返回值
具体使用方法

原生字符串字面值
  • cout << R"(hello\n world)" <<endl;
继承构造
  • 允许派生类继承基类的构造函数(默认,复制,移动构造函数除外)
  • 只能初始化基类的成员变量
    public:
    //原始写法
    B(int x,int y):A(x,y){

    }

    //c++11新写法 继承构造
    using A::A ;
委托构造函数
    //委托构造函数
    A():A(10,"mike"){
        cout << "我是无参构造函数" <<endl;
    };
    private:
    A(int a,string b):age(a),name(b){
        cout << "我是两个参数的构造函数"<<endl;
    };
继承控制 final 和 override
  • final:阻止类 的进一步派生和 虚函数 的进一步重写
  • override:确保在派生类中声明的函数跟基类有相同的函数签名
类默认函数的控制 default 和 delete 函数
  • delete 不仅可以修饰系统默认的函数,自己定义的类函数也可以修饰,作用是禁用某个函数
  • 当用户自定义了构造函数后,编译器就不会再提供构造函数,default就是让系统继续提供默认的类函数,效率比自己写的函数高
函数模板的默认模板参数
//c++11 支持普通的函数模板带默认参数
template<typename T = int> 
T TESTfunction(T a){return a};
//c++98 和 c++11都支持类模板中带默认的参数
//但是参数必须从右往左
template <typename T ,typename T2 = int>
class TEST{

};
  • 可变参数的模板函数
//可变参数的模板函数
template<typename ... T>   // T叫模板参数包
// args叫函数参数包
void function(T ... args){
    //获取可变参数的个数
    cout << sizeof ... (args) <<endl;
} 
  • 获取参数包的展开 非常重要
//可变参数模板的 参数包的展开
//递归的终止函数
void function2(){ 
    cout << "null" << endl;
}

template<typename  T1,typename ... T2>
void function2(T1 first ,T2 ... args){
    cout << first <<endl;
    //递归调用function2
    function2(args ...);
}

右值引用
  • 不能把一个左值赋值给右值引用
//左值引用
void test(int &tmp){
    cout << "左值 = " << tmp <<endl;
}

//右值引用
void test(int && tmp){
    cout << "右值 = " << tmp <<endl;
}

int main(){
    int a = 10;   //a 是一个左值
    test(a);
    test(256);    //256 是一个常数,是右值
    return 0;
}
返回值优化技术(移动语义)
  • 针对临时对象的维护会极大影响程序的运行速度(对象的创建和销毁)
  • 移动构造函数,但是现在的编译器一般都会自动进行优化。使用的主要还是右值引用。我们可以定义一个参数为右值的构造函数,在里面接管右值的资源。把右值引用作为函数参数,这就少了一次临时变量的构造的过程。
智能指针
  • unique_ptr:
  • shared_ptr:多个此对象可以同时绑定同一个堆区,可以用计数器来查看此堆区绑定了多少个shared_ptr。
  • weak_ptr: 主要是配合 share_ptr 来使用,它是没有重载 * 和 -> 运算符的,此对象不与堆区内存绑定,可以通过lock()函数来获取share_ptr对象,若use_count为空,则返回nullptr
#include<iostream>
//智能指针的头文件
#include <memory>
using namespace std;

int main(){
    /*
        unique_ptr
    */
    unique_ptr<string> ptr1 (new string("zhr"));  //创建一个int型的指针,指向堆区新开辟的这个空间 
    cout << "*ptr1 = " << *ptr1 <<endl;           //本质是ptr1这个对象里面有个指针指向这个堆区空间,然后重载 * 运算符。自动释放

    //unique_ptr<string> ptr2 = ptr1;     //err 禁止使用拷贝构造
    unique_ptr<string> ptr2 = std :: move(ptr1); //可以使用移动构造
    cout << "*ptr2 = " << *ptr2 <<endl; 

    //使用reset更改堆区的值         
    ptr2.reset(new string("nihao"));
    auto * p = ptr2.release();  //释放控制权,但是不释放堆内存
    cout <<"*p = " << *p << endl;


    /*
        shared_ptr
    **/
   shared_ptr<int> ptr3 (new int(22));
   shared_ptr<int> ptr4 = ptr3;
   //查看 use_count 计数器
   cout << "use_count: "<< ptr3.use_count() << endl;

   /* 
        weak_ptr ,主要是配合 share_ptr 来使用,它是没有重载 * 和 -> 运算符的,
        此对象不与堆区内存绑定,可以通过lock()函数来获取share_ptr对象,若use_count为空,则返回nullptr
   **/
    weak_ptr<int> wp = ptr3;
    cout << "use_count" << ptr3.use_count() <<endl; 
    //虽然wp和堆区空间不绑定,但是可以通过lock函数获取 shared_ptr<int> 对象
    decltype(ptr3) ptr5 = wp.lock();
    cout << "use_count:" <<ptr5.use_count() <<endl;

      
    return 0;
}
bind
#include <iostream>
//头文件
#include <functional>
using namespace std;
//bind(函数,占位符);
void func(int x, int y)
{
    cout << x << "," << y << endl;
}

class test
{
public:
    void func(int x, int y)
    {
        cout << x << "," << y << endl;
    }
    int a;
};

main()
{
    bind(func, 21, 22)();
    //占位符 std::placeholders::_1 ,std::placeholders::_2
    bind(func, std::placeholders::_1, std::placeholders::_2)(21, 22);
    bind(func, 21, std::placeholders::_1)(22);

    /*******************************************************/
    cout << "**************" << endl;
    //绑定成员函数
    test obj;
    function<void(int,int)> f = bind(test::func,obj,placeholders::_1,placeholders::_2);
    f(21,22);

    //绑定成员变量
    function<int&()> f2 = bind(&test::a,&obj);
    f2() = 22;
    cout << "obj.a = " << obj.a << endl;

    return 0;
}
lambda
  • 实际上,仿函数是编译器实现lambda的一种方式,用过编译器都是把lambda表达式转化为一个仿函数对象。
  • lambda表达式在 c++11中被称为 “闭包类型”,每一个lambda表达式会产生一个临时对象(右值),lambda函数并非函数指针,不过允许将lambda表达式向函数指针转换,但前提是没有捕获任何变量。
  • 利用typedef定义函数指针
lambda 表达式的优势 : 匿名函数
#include <iostream>
#include <functional>
#include <algorithm>  // foreach
#include <vector>
using namespace std;

/*  1,捕获列表 []
    2,参数 ()
    3,函数体{}
    4,如果有返回值的话在()后加 " -> 返回值类型 "
**/

//实际上,仿函数是编译器实现lambda的一种方式,用过编译器都是把lambda表达式转化为一个仿函数对象。
// lambda表达式在 c++11中被称为 “闭包类型”,每一个lambda表达式会产生一个临时对象(右值),lambda函数并非函数指针,
//不过允许将lambda表达式向函数指针转换,但前提是没有捕获任何变量。
int tmp = 2;
class test{
    public:
    int i {0};
    void func(){

        int inValue = 3;
        //[this] 可以捕获类成员变量,全局变量,不能捕获局部变量
        auto f8 = [this]{
            cout << i << "," << tmp <<endl;
            //cout << inValue << endl;
        };
        f8();

    }
};


int main()
{
    int age = 10;
    string name = "zhr";

    auto f = [age, name]()
    {
        cout << age << "," << name << endl;
    };
    f();

    auto f1 = [=]()
    {
        cout << age << "," << name << endl;
    };
    f1();

    auto f2 = [age, name](int x, int y)
    {
        cout << x << "," << y << endl;
    };
    f2(21, 22);

    //以值传递的方式传给lambda表达式 [=]
    auto f3 = [=]()
    {
        cout << age << "," << name << endl;
    };
    f3();
    //以引用的方式捕获 [&]
    auto f4 = [&]()
    {
        cout << age << "," << name << endl;
    };
    f4();

    //一部分以值传递,一部分以引用传递,下面的是一种错误的写法,&或者= 要放在前面
    auto f5 = [&,age]{
        cout << age << "," << name << endl;
    };
    f5();
    // auto f5 = [age,&]{
    //     cout << age << "," << name << endl;
    // };

    //默认情况下,lambda函数传入的参数都是以const修饰的,
    //值传递无法修改,想要修改加 mutable或者改为 引用传递
    auto f6 = [&]{
        age++;
        cout << age << "," << name << endl;
    };
    f6();
    //注意:此时不能省略()即使没有参数传入!!!
    auto f7 = [=]() mutable{
        age ++;
        cout << age << "," << name << endl;
    };
    f7();

    //验证 [this]
    test obj;
    obj.func();

    //将lambda表达式绑定到仿函数
    function<void(void)> f9 = [](){cout << "i am zhr!"<<endl;};
    f9();

    function<void(int)> f10 = [=](int x){cout << "i am x = "<< x << endl;};
    f10(22);

    //通过 bind 来绑定。    bind(函数,变量占位符)
    function<void(int)> f11 = bind([](int x){cout << "in bind,i am x = "<< x << endl;} , std::placeholders::_1);
    f11(22);

    auto f12 = [](int a,int b){return a+b;};

    //定义一个函数指针类型
    typedef int (*PFUNC)(int , int);
    PFUNC p1 = f12;   //lambda表达式转换为指针,此时不允许捕获外部变量 []
    cout << p1(21,22) << endl;

    /*
    lambda 表达式的优势 : 匿名函数
    **/
   vector<int> nums1;
   decltype(nums1) nums2;
    for(int i = 0;i < 10; i++){
        nums1.emplace_back(i);
    }

    int tmp = 5;

    for_each(nums1.begin(),nums1.end(),[&,tmp](int n){
        if(n > tmp){
            nums2.emplace_back(n);
        } 
    });

    cout <<endl;

    for_each(nums2.begin(),nums2.end(),[](int n){
        cout << n <<",";
    });


    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

强大的RGG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值