C++研究之---C++11 新特性

总结C++11新特性的功能用法和注意事项

  • auto 功能:类型说明符,用于自动获取表达式所属的类型

    //1.auto定义的变量必须有初始值
    auto item = val1 + val2;//item的初始化为val1和val2相加的结果
    
    //2.声明多个变量,每个变量类型要相同
    auto sz = 0,pi = 3.14;//错误:sz和pi类型不一致
    
    //3.使用引用是使用引用的对象
    int i = 0, &r = i;
    auto a = r; // a是一个整数
    
    //4.auto 忽略顶层const,保留底层const
    const int ci = i,&cr = ci;
    auto b = ci; //b是一个整数(ci的顶层const特性被忽略掉了)
    auto d = &i; //d是一个整形指针(整数的地址就是指向整数的指针)
    auto e = &ci; //e是一个指向整数常量的指针(对常量对象取地址是一种底层const)
    
    //一条语句定义多个变量,符号&和*只从属于某个声明符,而非基本数据类型,初始值必须是同一类型
    auto k = ci,&l = i;//k是整数,l是整数引用
    auto &n = i,*p2 = &ci;//错误,i的类型是int 而&ci的类型是const int
  • long long 类型 C++语言规定,一个int至少和一个short一样大,一个long至少和一个int一样大,一个long long至少和一个long一样大

  • 列表初始化 用花括号来初始化变量

    //各种初始化
    int units_sold = 0;
    int units_sold = {0};
    int units_sold{0};
    int units_sold(0);
    
    //列表初始化如果存在丢失信息的风险,则报错:
    long double ld = 3.1415926;
    int a{ld},b = {ld};//错误:转换示执行,因为存在丢失信息的危险
    int c(ld),d = ld; //正确:转换执行,丢失部分数值
  • 空指针 nullptr 特殊类型的字面值,可以转换为任意指针类型; C++程序最好使用nullptr,尽量避免使用NULL;

    //下面等价
    int *p1 = nullptr;
    int *p2 = 0;
    //需要#include cstdlib
    int *p3 = NULL
  • constexpr 变量 constexpr声明的变量由编译器来验证变量值是否是一个常量表达式; constexpr声明的变量一定是一个常量,而且必须用常量表达式初始化; constexpr声明的类型为字面值类型(算术,引用 和指针),自定义、IO库,string类型不属于。 constexpr指针初始值必须是nullptr或0,或是固定地址的对象(函数体之外的对象)

    constexpr int mf = 20;//20是常量表达式
    constexpr int limit = mf + 1; //mf +1 是常量表达式
    constexpr int sz = size(); //只有当size是一个constexpr函数时才是一条正确的声明语句
    
    //仅对指针有效,与指对象无关
    const int *p = nullptr; //p是一个指向整形常量指针
    constexpr int *q = nullptr;//q 是一个指向整数的常量指针
    
    //constexpr指针既可以指向常量也可以指向一个非常量
    constexpr int *np = nullptr;//np是一个指向号数的常量指针
    int j = 0;
    constexpr int i = 42;
    //i和j 都必须定义在函数体之外
    constexpr const int *p = &i;//p是常量指针,指向整型常量i
    constexpr int *p1 = &j;、、//p1是常量指针,指向整数j
  • 类型别名using

    //传统方法 typedef
    typedef double wages;//wages是double的同义词
    
    //using
    using SI = Sales\_item; //SI是Sales\_item的同义词
    
    //指针、常量和类型别名
    typedef char *pstring;//pstring 是指向char 的指针
    const pstring cstr = 0;//cstr是指向char的常量指针
    const pstring *ps;//ps是一个指针,它的对象是指向char的常量指针
    //如果理解成:const char *cstr = 0;//是对const pstring cstrr 的错误理解
  • decltype 类型指示符 选择并返回操作数的数据类型

    decltype(f()) sum = x; //sum的类型就是函数f的返回类型
    
    //decltype处理顶层const和引用的方式与auto不同
    const int ci = 0,&cj = ci;
    decltype(ci) x = 0; //x是const int
    decltype(cj ) y = x; //y是const int&,y绑定到变量x
    decltype(cj) z; //错误 z是一个引用,必须初始化
    
    //如果decltype使用的表达式不是一个变量,则以返回的表达式结果对应类型
    int i = 42,*p = &i,&r = i;
    decltype(r + 0) b; //正确:加法的结果是int,因此b是一个int
    decltype(*p) c; //错误:c是int& 必须初始化
    
    //decltype 的表达式如果是加上了括号的变量,结果将是引用 
    decltype((i)) d;//错误:d是int&,必须初始化
    decltype(i) e;//正确:e是一个(未初始化的)int
    
    //使用decltype简化函数返回类型
    int odd\[\] = {1,3,5};
    int even\[\] = {0,2,4};
    //返回一个指针,指向含有3个整数的数组
    decltype(odd) *arrPtr(int i ){
        return (i%2)?&odd:&even;
    }
  • 类内初始化 初始化和赋值不同,一个是没值,一个是有值

  • 范围for 遍历给定序列中的每个元素并对序列中的每个值执行某种操作

    //语法
    for(declaration : expression)
        statement
    
    //示例
    string str("some string");
    for(auto c : str) cout << c <<endl;
    for(auto &c : s) c = toupper(c);//转为大写
  • vector对象的vector

    vector<vector<int>>
  • vector对象列表初始化

    vector<string> articles = {"a","an","the"};
    vector<string> v1("a","an","the");//错误
  • 容器的cbegin和cend函数 类似于begin和end,只是返回的是const_iterator

    auto it2 = v.cbegin();//it3的类型是vector<int>::const_iterator
  • 函数begin和end 和容器功能类似,以数组为参数

    int ia\[\] = {0,1,2,3,4,5,6,7};
    int *beg = begin(ia);//指向首元素的指针
    int *last = end(ia);//指向尾元素的下一个位置的指针
    
    //示例
    //pbeg指向arr的首元素,pend指向arr的尾元素的下一位置
    int \*pbeg = begin(arr),\*pend = end(arr);
    //寻找第一个负值元素
    wile(pbeg != pend && *pbeg >=0) ++pbeg;
  • 除法舍入规则 商一律向0取整(直接切除小数部分)

  • sizeof用于类成员 使用作用域运算符来获取类成员的大小,sizeof运算符无须提供一个具体的对象。

  • initializer_list类 标准库类型,用于表示某种特定类型的值的数组

    //是一种模板类型,注意元素是常量值
    initializer\_list<string> ls;//initializer\_list的元素类型是string
    initializer_list<int> li;
    
    //示例
    void error\_msg(initializer\_list<string> il)
    {
        for(auto beg = il.begin();beg!= il.end();++beg) cout << *beg << "";
        cout <<endl;
    }
    //调用 expected 和actual是string对象
    if(expected != actual) error_msg({"functionX",expected,actual});
    else error_msg({"fuctionX","okay"});
  • 列表初始化返回值 函数可以返回花括号包围的值的列表

    vector<string> process()
    {
        return {};
        //return {"fuctionX","okay"};
    }
  • 尾置返回类型 较适用于返回类型比较复杂的函数(数组的指针或数组的引用)

    //func接受一个int类型的实参,返回一个指针,该指针指向含有10个整数的数组
    auto func(int i)->int (*)\[10\];//注意加括号和不加括号的区别
  • constexpr函数 能用于常量表达式的函数:函数返回类型及所有形参的类型都得是字面值类型;函数体必须有且只有一条return语句

    constexpr int new_sz() {return 42;}
    constexpr int foo = new_sz();//正确:foo是一个常量表达式
    
    //函数体内也可以包含其他语句,只有这些语句在运行时不执行任何操作(空语句,类型别名,using声明)
    //如果arg是常量表达式,scale(arg)也是常量表达式
    constexpr size\_t scale(size\_t cnt){return new_sz() * cnt}
    
    //当scale的实参是常量表达式时,它的返回值也是常量表达式
    int arr\[scale(2)\]; //正确
    int i = 2;
    int a2\[scale(i)\]; //错误,scale(i)不是常量表达式
  • =default 生成默认构造函数 通过在参数列表后面写上=default,要求编译器生成构造函数。可以和声明一起出来在类的内部(内联),也可以作为定义出现在类的外部。

    Sales_data() = default;
  • 类对象成员类内初始化

    class Window_mgr{
    private:
        //这个Window_mgr追踪的Screen
        //默认情况下,一个Window_mgr包含一个标准尺寸的空白Screen
        std::vector<Screen> screens{Screen(24,80,'')};
    }
  • 委托构造函数 使用它所属类的其他构造函数执行它自己的初始化过程(把它职责委托给了其他构造函数)

    class Sales_data{
    public:
        //非委托构造函数使用对应的实参初始化成员
        Sales\_data(std::string s,unsigned cnt,double price):bookNo(s),units\_sold(cnt),revenue(cnt *price){}
        //委托构造
        Sales\_data():Sales\_data("",0,0){}
  • constexpr构造函数 符合构造函数要求(没有返回语句),符合constexpr函数要求(拥有唯一可执行语句就是返回语句),一般函数是空的。可声明=default或=delete

    //constexpr构造函数必须初始化所有数据成员,初始值使用constexpr构造函数或一条常量表达式
    class Debug{
    public:
        constexpr Debug(bool b = true):hw(b),io(b),other(b){}
        constexpr bool any(){return hw||io||other;}
    }
    
    //constexpr构造函数用于生成constexpr对象以及constexpr函数的参数或返回类型
    constexpr Debug io_sub(false,true,false);
    if(io_sub.any()) cerr<<"print appropriate error message"<<endl;
  • 用string对象处理文件名 IO参数可以使用库类型string的对象

  • array和forward_list容器 与内置数组相比,array是一种更安全、更容易使用的数组类型;array对象的大小是固定的,不支持添加和删除元素以及改变容器的大小操作。 forward_list目标是达到与最好手写的单向链表数据结构相当的性能,没有size操作

  • 容器的cbegin和cend函数 无论容器元素是否是常量,返回值都是const_iterator

    auto it3 = v.cbegin(); //it3的类型是vector<int>::const_iterator
  • 容器的列表初始化

    list<string> authors = {"M","S","A"};
    vector<const char*> art = {"a","an","the"};
  • 容器的非成员函数swap swap操作交换两个相同类型容器的内容。调用后,元素交换(本身未交换,只是交换了两个容器的数据结构)

    vector<string> svec1(10); //10个元素
    vector<string> svec2(24);
    swap(svec1,svec2);
  • 容器insert成员的返回类型 insert返回指向第一个新加入元素的迭代器,如果范围为空,不插入任何元素,insert操作会将第一个参数返回。

    list<string> lst;
    auto iter = lst.begin();
    while(cin >> word)
        iter = lst.insert(iter,word);//等价于调用push_front
  • 容器的emplace成员的返回类型 emplace_front,empace和emplace_back 操作构造元素(相对于push或insert拷贝),对应push_front,insert和push_back,将元素旋转在容器的头部、一个指定位置之前或窗口尾部

  • shrink_to_fit 适用于vector,string和deque,capacity和reserve只适用于vector和string

    c.shrink\_to\_fit();//将capacity()减少为与size()相同的大小
  • string的数值转换函数

    int i = 42;
    string s = to_string(i);//将整数i转换为字符表示形式
    double d = stod(s);//将字符串s转换为浮点数
    
    //转换s中以数字开始的第一个子串,结果d = 3.14
    string s2 = "pi = 3.14";
    d = stod(s2.substr(s2.find\_first\_of("+-.0123456789")));
  • lambda匿名函数 可调用对象(callable object)(函数,函数指针,重载运算符的类,lambda表达式) lambda:未命名的内联函数,可定义在函数内部

    //capture list(捕获列表),通常为空; 
    \[capture list\](parameter list) -> return type{function body}
    
    //可忽略参数列表和返回类型,但必包含捕获列表和函数体
    //空参数列表,推断返回类型
    auto f = \[\] {return 42;};
    cout << f() <<endl;//调用
    
    //传参
    \[\](const string &a,const string &b){return a.size()< b.size()}
    //调用示例
    stable_sort(words.begin(),words.end(),\[\](const string &a,const string &b){return a.size<b.size();});
    
    //捕获列表
    \[sz\](const string &a){ return a.size >=sz;};
    
    //调用find_if
    auto wc = find_if(words.begin(),words.end(),
         \[sz\](const string &a){ return a.size >=sz;})
    
    //for_each
    for_each(wc,words.end(),
        \[\](const string &s){cout << s << " ";});
    cout << endl;
    
    //值捕获
    void fcn1()
    {
        size_t v1 = 42;
        auto f = \[v1\]{return v1;};
        v1 = 0;
        auto j = f();//j为42;
    }
    
    //引用捕获
    void fcn2(){
        size_t v1 = 42;
        auto f2 = \[&v1\]{return v1;};
        v1 = 0;
        auto j = f2();//j为0,
    }
    
    //隐式捕获
    //sz为隐式捕获,值捕获方式
    wc = find_if(words.begin(),words.end(),
        \[=\](const string &s){return s.size()>=sz;})
    
    //可变lambda
    void fcn3(){
        size_t v1 = 42;
        //f可以改变它所捕获的变量的值
        auto f = \[v1\]() mutable{return ++v1;};
        v1 = 0;
        auto j = f(); //是为43;
    }
    
    //尾置返回类型
    transform(vi.begin(),vi.end(),vi.begin(),
        \[\](int i) -> int
        {if(i<0) return -i; else return i;});
  • 标准bind 在functional文件中定义,通用的函数适配器,接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表

    auto new Callable = bind(callable,arg_list);
    
    //check6是一个可调用对象,接受一个string类型的参数
    //并用此string和值6来调用check_size
    auto check6 = bind(check\_size,\_1,6);//_1参数占位符
    
    string s = "hello";
    bool b1 = check6(s);//check6(s)会调用check_size(s,6);
    
    //lambada的find_if
    auto wc = find_if(words.begin(),words.end(),\[sz\](const string &a))
    //替换为
    auto wc = find\_if(words.begin(),words.end(),bind(check\_size,_1,siz));
    
    //_n为placeholders命名空间所定义,functional头文件中
    using std::placeholders::_1;
    //使用所有的定义
    using std::placeholders
    
    //bind的参数
    //g是一个有两个参数的可调用对象
    auto g = bind(f,a,b,\_2,c,\_1);
    //示例
    //g(\_1,\_2) 映射为
    //f(a,b,\_2,c\_1)
    
    //用bind重排参数顺序
    //按单词长度由短到长排序
    sort(words.begin(),words.end(),isShorter);
    //按单词长度由长到短排序
    sort(words.begin(),words.end(),bind(isShorter,_2,-1));
    
    //绑定引用参数,ref和cref(const引用类) 定义与functional
    //为了替换一个引用方式捕获ostream的lambda:
    //os是一个局部变量,引用一个输出流
    //c是一个局部变量,类型为char
    for_each(words.begin(),words.end(),\[&os,c\](const string &s){os << s << c;});
    //相同的函数
    ostream &print(ostream &os,const string &s,char c){
        return os<<s <<c;
    }
    //用bind代替os的捕获是错误的,os不能拷贝
    for\_each(words.begin(),words.end(),bind(print,os,\_1, ''));
    //可以使用标准的ref函数
    for\_each(words.begin(),words.end(),bind(print,ref(os),\_1,''));

待续… 汇总自:C++Primer 第五版


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 xue_huashan@163.com

文章标题:C++研究之---C++11 新特性

文章字数:3.5k

本文作者:max-xue

发布时间:2018-08-22, 13:57:02

最后更新:2019-11-09, 21:09:39

原始链接:http://blog.le-more.com/2018/08/22/c++/c11-e6-96-b0-e7-89-b9/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏