当前位置:C++技术网 > 资讯 > C++11学习笔记

C++11学习笔记

更新时间:2015-09-23 16:02:03浏览次数:1+次

1.long long新类型

2.列表初始化

       int t=0;

    int t={0};

    int t(0);

      int t{0};

                        注意:如果我们使用列表初始化有丢失信息的风险,则编译器报错

       long double ld=3.1415926536;

    int a{ld},b={ld};//错误

      int c(ld),d=ld;//正确,会丢失数据

3.空指针nullptr

       int *p1=nullptr;

    int *p2=0;

      int *p3=NULL;//尽量避免

4.constexpr类型

            const类型不一定是常量表达式,常量表达式指的是在编译的时候就知道值大小

            如:

                        constint sz=str.size();

            str的长度如果在编译的时候无法确定,则sz就不是常量表达式

            而声明为constexpr的变量一定是个常量:

              constexprint mf=20;//20是常量表达式

       constexprint limit=mf+1;//mf+1是常量表达式

            constexprint sz=size();//只有size()是一个constexpr函数时才能编译通过

            指针与constexpr

                        如果用constexpr声明了一个指针,则constexpr仅对指针有效(注意与const的区别)

                     constint *p=nullptr;//p是一个指向整型常量的指针

                  constexprint *q=nullptr;//q是一个指向整形的常量指针

                        如果要声明一个指向常量的常量指针,使用:

                                    constexprconstint * q=&i;

5.类型别名

            传统方法是使用typedef

                        typedefunsignedint UINT;

            C++11规定一种新方法:

                        using UINT=unsignedint;

            注意:指针、常量、类型别名

       typedefchar *pstring;

    const pstring cstr=0;//指向char的常来那个指针

      const pstring *ps;//ps是一个指针,对象是一个指向char的常量指针

6.auto类型说明符

            auto定义的类型必须要有初始值,因为编译器要分析实际类型

              auto i=0;//iint

       auto j=3.14;//jdouble

            auto k;//错误

            auto可以同时声明多个变量,但是同时声明的所有变量类型要相同

              auto i=0,*p=&i;//正确

            auto sz=0;pi=3.14;//错误

            复合类型、常量和auto

                        引用:

                     int i=0,&r=i;

                  auto a=r;//a为整型

                        const,auto会忽略顶层const,底层const会保留

                     constint ci=i,&cr=ci;

           auto b=ci;//b为整型ci顶层const特性被忽略

           auto c=cr;//c为整型crci的别名

           auto d=&i;//d是一个整型指针i不为常量

                  auto e=&ci;//e是一个指向整型常量的指针常量对象取地址是底层const

                        如果希望推断出的auto类型是顶层const,需要明确指出:

                                    constauto f=ci;//ci的推演类型为int,而fconst int

                        将引用类型设为auto,原来的初始化规则仍然适用

                     auto &g=ci;//g是整型常量引用,绑定到ci

           auto &h=42;//错误,不能为非常量引用绑定字面值(忽略顶层const

                  constauto &j=42;//正确常量引用绑定字面值

                        注意,声明多个变量时,*与&是声明符的一部分,而不是基本数据类型的一部分

                     auto k=ci,&l=i;//正确

           auto &m=ci,*p=&ci;//正确

                  auto &n=i,*p2=&ci;//错误iint&ciconst int *

7.decltype类型指示符

            decltype推断变量(函数)类型

                        decltype(f()) sum=x;//sum的类型就是f的返回值类型

            注意,decltype与auto对const处理的区别:auto会忽略顶层const,而decltype会保留顶层const

              constint ci=0,&cj=ci;

       decltype(ci) x=0;//xconst int

       decltype(cj) y=x;//yconst int&

            decltype(cj) z;//错误zconst int&必须绑定值

            引用一直是以其所指对象的同义词出现,但是对于decltype并非如此(会保留引用类型)

            如果表达式的内容是解引用操作,decltype会得到引用类型

              int i=42,*p=&i,&r=i;

       decltype(r+0) b;//加法的结果是int,所以b是一个未初始化的int

            decltype(*p) c;//错误cint&

            注意:decltype((variable))(注意是双层括号)的结果是引用,而decltype(variable)的结果只有当variable本身是一个引用是才是引用

              decltype((i)) d;//错误dint&需要绑定值

            decltype(i) e;//正确e是一个未初始化的int

8.类内初始值

            C++11规定,可以为类内数据成员提供一个初始值用于初始化,如果没有提供,将进行默认初始化

            注意:不能使用圆括号,可以使用花括号或等号

9.使用auto或decltype缩短类型长度

            同时可以避免无符号数与有符号数可能带来的问题

10.范围for

       for(declaration:expression)

            statement

            如果想要用范围for改变对象中的值,需要把循环变量定义为引用

              string s("hello world");

       for(auto &c:s){

           c=toupper(c);

       }

            cout<<s<<endl;

11.定义vector对象的vector

            在C++11标准之前,如果要定义一个vector的vector,必须在两个>之间添加空格:

                        vector<vector<int> > arr;

            而新标准不用这样操作:

                        vector<vector<int>> arr;//C++11合法

12.vector的列表初始化

            注意区分圆括号与花括号

              vector<int> v1(10);//10个元素,都是0

       vector<int> v1{10};//一个元素,10

       vector<int> v1(10,1);//10个元素,都为1

            vector<int> v1{10,1};//两个元素,10,1

            使用花括号时,初始化过程会尽可能把花括号内的值当成元素初始值的列表来处理,只有在无法执行列表初始化时才会考虑其他初始化方式。

              vector<string> v5{"hi"};//列表初始化

       vector<string> v6("hi");//错误

       vector<string> v7{10};//转化为圆括号

            vector<string> v7(10);//10个空字串

13.容器的cbegin与cend函数

            cbegin和cend返回const迭代器,当不需要对元素进行修改时,最好使用const迭代器

14.标准库函数begin与end

            在容器中,有两个同名函数,功能类似,但是标准库begin与end不是成员函数,它的参数为数组

              int ia[]={1,2,3,4,5,6,7,8};

       int *beg=begin(ia);

            int *last=end(ia);

            这两个函数定义在iterator中

15.使用auto和decltype简化声明

       int ia[3][4];//大小为3的数组每个元素都是含有4个整数的数组

    int (*p)[4]=ia;//p指向含有4个整数的数组

      p=&ia[2];//p指向ia的尾元素

            在上述声明中圆括号必须有

              int *ip[4];//整型指针的数组

            int (*p)[4];4个整数的数组的指针

            可以用auto自动推断类型,避免出错

16.除法舍入规则

            C++11规定整数除法一律向0取整(即切除小数部分)

17.初始值列表赋值

       int k=0;

    vector<int> vi;

    k={5};//正确

    k={3.14};//错误,窄化转换

      vi={0,1,2,3,4};//正确

18.sizeof用于类成员

            sizeof可用于类成员而不需要类的对象(想知道类成员大小并不需要真的获取类成员)

19.范围for 2

            不能通过范围for添加或删除元素,因为范围for存储了end()的值,增加或删除会使迭代器失效

20.标准库initializer_list类

            如果实参数量未知但是类型都相同,可以使用initializer_list类型形参

              void error_msg(initializer_list<string> il){

           for(auto p:il){

              cout<<p<<endl;

           }

       }

 

       error_msg({"hello","world"});

            error_msg({"I","love","you"});

      使用vector等也可以

21.列表初始化返回值

       vector<string> func(){

       return {"hello","world"};

      }

22.尾置返回类型

              int func(int a){

           return0;

            }

            如果使用尾置返回类型则可以写作:

              auto func(int a)->int{

           return0;

            }

            对于返回值类型比较复杂的函数最有效,如:返回数组指针的函数

              int (*func(int i))[10]{

           //函数体

            }

            等价于

              auto func(int i)->int(*)[10]{

           //函数体

            }

23.使用decltype简化返回类型定义

            我们知道返回的类型指向哪个数组,就可以使用decltype关键字声明返回类型

              int odd[]={1,3,5,7,9};

       int even[]={0,2,4,6,8};

       decltype(odd) *arrPtr(int i)}{

           return (i%2)?&odd:&even;

            }

            注意:decltype并不负责吧数组转换为对应的指针,所以要在函数声明时加一个*

24.constexpr函数

            constexpr函数是指能用于常量表达式的函数,函数的返回值以及所有的参数都必须是字面值类型,函数体中必须有且只有一条return语句

            但是,C++11允许constexpr返回值并非一个常量

              constexprint new_sz(){

           return42;

       }

       constexpr size_t scale(size_t cnt){//如果cnt是常量则返回常量

           return new_sz()*cnt;

       }

       int arr[scale(2)];//正确

       int i=2;

            int a2[scale(i)];//错误scale(i)不是常量表达式

            注意:inline、constexpr可以在程序中多次定义,但是多次定义必须完全一致,所以内联函数和constexpr函数通常放在头文件中

25.使用=default生成默认构造函数

            如果自定义了类的带参构造函数,则编译器不会生成默认构造函数,这时可以用=default生成默认构造函数

              class A{

           A(int t);

           A()=default;//会生成默认构造函数

            };

            =default可以在类内部声明定义,也可以在类外部定义,如果在类内部,则默认构造函数是内联的,否则是非内联的(其他成员函数也是如此)

26.类内成员初始化

            当我们提供一个类内初始化值时,必须使用等号或花括号表示(不能使用圆括号)

27.委托构造函数

            委托构造函数是指把部分(或全部)职责委托给其他构造函数

              class A{

           A(int a);

           A():A(0){};

            };

28.constexpr构造函数

            含有constexpr构造函数的类叫constexpr类,constexpr构造函数既要满足constexpr函数的要求又要满足构造函数的要求,,所以constexpr构造函数的函数体一般是空的

            constexpr构造函数用于生成constexpr对象以及constexpr函数的参数或返回类型

29.用string对象作为文件名

            在旧的C++标准中,文件名只能是C风格字符串

              cosnt char * const fileName="a.txt";

       ifstream fi(fileName);

       string  fileName2="b.txt";

            ifstream fi(fileName2.c_str());

            在新的C++标准中,文件名既可以是string对象,也可以是C风格字符串

              string  fileName3="c.txt";

            ifstream fi(fileName3);//C++11中正确

30.array与forward_list

            array与forward是C++新标准添加的内容,array替代内置数组,大小固定,但更安全,更易使用,forward替代手写链表,不支持size()

            C++程序应该使用标准库容器而不是原始数据结构,如:内置数组

            通常,使用vector是最好的选择,除非有更好的理由选择其他容器

31.容器的cbegin与cend

            以前的C++标准中,const对象的begin()返回const类型的迭代器,否则返回普通iterator,如果要使用const迭代器,必须显示指定。由于新标准中添加了auto类型,所以同时添加了cbegin和cend,用于返回一个非const(const同样适用)对象的const迭代器

            当不需要写访问时,尽量使用cbegin和cend

32.容器的列表初始化

            容器用列表初始化时,除了array外,初始化列表隐含制定了容器大小

33.标准库swap

            成员函数swap会交换内部数据结构,不会交换元素(array除外),除string外,指向容器的迭代器、引用和指针在swap操作后都不会失效

            array对象swap时间与array对象的元素数量成正比

            标准库swap在泛型编程中非常重要

            统一使用非成员swap(标准库swap)是个好习惯

34.insert的返回值

            insert操作后,会返回插入元素的迭代器

35.容器的emplace成员

            emplace_back使用已有空间重新构造

            push_back创建一个临时对象并且把它压入容器

36.shrink_to_fix

            shrink_to_fix将capacity()减小为与size()相同的大小

37.string数值转换

            标准库函数:

              to_string(val);

       stoi(s,p,b);

       stol(s,p,b);

       stoul(s,p,b);

       stoll(s,p,b);

       stoull(s,p,b);

       stof(s,p,b);

       stod(s,p);

            stold(s,p);

            val为要转换的数字,s为要转换的字符串,p为s中第一个非数值字符的下标,b是转换基数

38.lambda表达式

            [捕获列表](参数列表)->返回值类型{函数体}

            lambda表达式可以使用捕获列表中列出的局部变量

                        直接写变量名是值捕获,加上&后是引用捕获,类似函数参数传递,使用引用捕获时,必须确保lambda执行时变量是存在的

            尽量保持捕获简单化,尽量避免指针捕获与引用捕获

            捕获列表中如果是=或&表示根据代码推断要捕获哪些内容,其中=是值捕获,&是引用捕获

            捕获有以下形式,=和&必须是列表中的第一个,表示隐式捕获的捕获类型

              []

       [names]

       [&names]

       [&]

       [=]

       [&,name]

            [=,&name]

            可用mutable形成可变lambda,这样的lambda表达式即使是值捕获也可以改变变量的值 

            一个引用捕获的变量是否可以修改依赖于此引用指向的是一个const类型还是一个非const类型

39.lambda表达式必须使用尾置返回类型

            如果lambda表达式中只有一个单体return或无返回语句,可以省略返回值类型,否则必须使用尾置返回类型

40.标准库bind函数

            bind定义在<functional>中

            bind可以看作一个通用的函数适配器

            一般形式为:

                        auto newCallable=bind(callable,arg_list);

            newCallable是一个新的可调用对象,arg_list是一个以逗号分隔的参数列表

            当我们调用newCallable时,newCallable会调用callable,并传递给它arg_list参数

            arg_list中的参数可能包含_n的名字,其中n为整数,这些参数是占位符,表示newCallable的参数,其中_1表示第一个参数,_2表示第二个参数,以此类推

            占位符定义在placeholder命名空间中,而placeholders定义在std中,所以需要使用usingnamespace std::placeholders;

              void func(string str1,int num1);

       auto newFunc=bind(func,_1,5);

            newFunc("hello");//等价于调用func("hello",5);

            基于bind的特点,可以用它重排参数顺序

            bind拷贝其参数,如果参数需要是引用类型,则需要ref或cref(也定义在<functional>中)

              ostream &print(ostream &os,const string&s,char c){

           return os<<s<<c;

       }

            for_each(words.begin(),words.end(),bind(print,ref(os),_1<' '));

41.关联容器的列表初始化

       map<int,int> mp={

              {1,3},

              {2,4}

                              };

42.pair返回值

            新标准中,可以使用列表对返回值进行初始化

              pair<string,int> process(vector<string> &v){

           if(!v.empty()){

              return {v.back,v.back().size()};

           }else{

              return pair<string,int>();

           }

            }

43.pair的列表初始化

            对map进行insert时,要记住类型是pair,构成pair最简单的办法就是参数列表中使用花括号初始化

       map<string,int> mp;

      mp.insert({"hello",5});

44.无序容器

            新标准定义了4个无序容器:unordered_mapunordered_setunordered_multimapunordered_multiset

            如果关键字类型固有就是无序的,或者性能测试发现问题可以用哈希技术解决,就可以使用无序容器

            无序容器对关键字的要求:

                        可以使用任意内置类型,但不能使用自定义类型,不过可以通过通过函数来替代==运算符和哈希值计算函数。

            hash<key_type>是标准库提供的hash值生成函数

                        unordered_set<Foo,decltype(FooHash)*>fooset(10,FooHash);//使用FooHash生成哈希值,Foo必须有==运算符

45.智能指针

            新标准提供了两种智能指针:

                        shared_ptr:允许多个指针指向同一个对象

                        unique_ptr:“独占”所指对象

            标准库还定义了一种名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象

            这三个类型都定义在<memory>头文件中

            shared_ptr会自动释放管理的内存,但是有两种情况例外:

                        (1).没有销毁不用的shared_ptr对象,如将其作为返回值却没有使用

                        (2).将shared_ptr放在一个容器中,却没有释放容器中的资源

46.动态分配内存列表初始化

            int *arr=newint[5]{1,2,3,4,5};

47.auto与动态分配

            如果我们提供了一个用括号包围的初始化器,就可以使用auto,但是只有括号中仅有单一初始化对象时才可以使用auto

              auto p1=newauto{5};

            auto p2=newauto{5,6};//错误,无法推断类型

48.unique_ptr类

            在同一时刻,只能有一个unique_ptr指向一个对象,当unique_ptr被销毁时,他所指的对象也被销毁了

            unique_ptr不支持赋值

              unique_ptr<string> p1(new string("hello world"));

       unique_ptr<string> p2(p1);//错误,unique_ptr不支持拷贝

       unique_ptr<string> p3;

            p3=p1;//错误unique_ptr不支持赋值

            不能拷贝的规则有一个例外,可以拷贝一个将要被销毁的unique_ptr(函数返回一个局部的unique_ptr)

              unique_ptr<int> close(int p){

           return unique_ptr<int>(newint(p));

            }

49.weak_ptr类

            weak_ptr指向一个shared_ptr管理的对象,但是不会为shared_ptr增加引用计数,所以weak_ptr可能指向一个一个已经被释放的内存区

            由于weak_ptr指向的内存区可能已经被释放,所以要通过lock成员函数判断对象是否还存在

              if(shared_ptr<int> np=wp.lock()){//如果np不为空条件成立

           //if中,npp共享对象

            }

50.不能用范围for处理动态数组中的元素

            new分配的所谓的数组其实不是数组,而是一个指向一块内存区的指针,由于不是数组类型,所以不能对动态数组调用begin和end(标准库函数),这些函数使用维度来返回指向首元素和尾后元素的指针,出于相同原因,也不能用范围for处理(所谓的)动态数组中的元素

            动态数组不是数组类型

51.动态数组的初始化

            可以采用列表初始化

                        int *p=newint[10]{1,2,3,4,5,6,7,8,9,0};

52.auto不能用于动态数组

            可以用空括号对数组中的元素进行值初始化,但是不能栽括号中给出初始化器,这意味着不能用auto分配数组(auto无法推断类型)

53.allocator::construct调用任意构造函数

            allocator(定义在<memory>中)可以使内存分配和对象构造分开

       int n=10;

    allocator<string> alloc;//可以分配string对象的allocator对象

    autoconst p=alloc.allocate(n);//分配n个未初始化的string

    auto q=p;

    alloc.construct(q++);// *q为空串

    alloc.construct(q++,10,c);// *q"cccccccccc"

      alloc.construct(q++,"hello");//*q"hello"

            在分配内存后,必须使用construct构造对象

            对象的销毁使用destroy(只能对已经构造了的对象使用destroy)

54.将=default用于拷贝控制成员

            通过将拷贝控制成员定义为=default来显式地要求编译器生成合成的版本

55.使用=delete阻止拷贝 

            在新标准下,我们可以通过将拷贝构造函数和拷贝赋值运算符定义为删除的函数来阻止拷贝。

            删除的函数是这样一种函数:虽然我们声明了他们,但是不能以任何方式使用它们。

            在函数参数列表后面加上=delete来指出我们希望它定义为删除的

              struct NoCopy{

           NoCopy()=default;//使用默认构造函数

           NoCopy(const NoCopy&)=delete;//阻止拷贝

           NoCopy &operator(const