C++11 新特性总结

0】g++/gcc 支持c++11/c11标准

{

(0)为了让g++能够支持和编译c++11新特性代码,需在编译时候加上指定条件:-std=c++11

g++ newFeature.cpp -o app -std=c++11

(1)使用-std=c11来让gcc支持c11新特性

gcc 文件名.c -o 可执行文件名 -std=c11

}

1】静态断言(static_assert)#include

{

(1)断言概念

     所谓“断言”,它是一种表现行为,亦是一种编程手段。它总是将需要“返回值为真的判别式”放到语句中,其所扮演的角色就是告知系统当程序的某个条件其值不为真的时候,退出程序,不再继续执行下去。需要知晓的是,断言并不是代码中必不可缺的部分,因为它是可替代的,用if(条件判断)、#error(预处理宏)也能完成断言同样的效果。存在即合理,断言必然有它存在理由。它可以快速定位到程序出错的位置,比 if 更简洁,比 #error 更加强大、适用。

(2)静态断言satic_assert 与运行时断言assert

     在C++11之前,assert断言最为我们熟悉,static_assert是在C++11中才引入的新特性。欲识其“人”先观其貌,从语句的声明上,差异不大。以下分别是assert和static_assert的声明格式:                     

从声明上, assert 仅是比 static_assert 少了一个参数

{

(2-1)运行时断言assert

{               

1.assert 是属于运行时断言,只有程序在运行时且调用了该函数,才能生效;

2.当且仅当 expr 条件不为真(true)时候,才会执行报错并终止进程;

3.可使用 NDEBUG 宏来告知编译器不进行 assert 断言判断,即取消assert断言功能.

从图3可看到其报错信息共囊括了:文件名、断言行号、函数名及断言条件。根据include.h 文件中 assert 的声明可以看到,用到了宏: __FILE__ __LINE____func__, 如图4所示。

}

(2.2)静态断言satic_assert

{

1.库可以在编译时检测常见的使用错误

2.c++标准库的实现可以检测和诊断常见的使用错误,提高可用性

3.可以声明静态断言来在编译时检查重要的程序不变量

4.static_assert中的断言表达式必须是在编译器可以计算的表达式,即常量表达式。若使用变量,则会报错。

 

}

}  

}

2】基于范围的for循环

{

(1)基于范围的for循环

{

(0)特点及要求:

{

(1)和普通循环一样,也可以采用continue跳出循环的本次迭代。

(2)用break来终止整个循环

(1)for循环迭代的范围是可以确定的;如数组的第一个元素和最后一个元素便构成了for选好的迭代范围。

(2)对于用户自定义的类,若类中定义便实现的有begin、end函数,则这个begin、end便是for循环的迭代范围。

(3)基于范围的for循环要求迭代器的对象实现:++ ==等操作符。

(4)对于STL标准模板库中(如:vector,set,list,map,queue,deque,string等)的各种容器使用“基于范围的for循环”是不会有

任何问题的,因为这些容器中都定义了相关操作。

}

(1)不必去操心数组越界(边界)问题,因此非常的方便

{

语法形式:

for(declaration:expression)

{ statement }

expression部分表示一个对象,用于表示一个序列。

declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。

每次迭代,declaration部分的变量会被初始化为expression部分的下一个

元素值。

示例1:

{

#include

#include

#include

using namespace std;

int main()

{

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

for (auto val : arr)

{

cout << val << " ";

}

system("pause");

return 0;

}

 

输出结果:

1 2 3 4 5 6 7 8 9 10

}

示例2:若迭代器变量的值希望能够在for中被修改,可以采用引用&的方式;

{

#include

#include

#include

using namespace std;

int main()

{

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

for (auto &val : arr)

{

if (val == 5)

{

val += 1;

}

cout << val << " ";

}

system("pause");

return 0;

}

 

输出结果:

1 2 3 4 6 6 7 8 9 10

}

示例3:对于STL标准模板库也同样适用。

{

#include

#include

#include

#include

using namespace std;

int main()

{

vector arr;

arr.push_back(1);

arr.push_back(3);

arr.push_back(5);

arr.push_back(7);

arr.push_back(9);

for (auto &val : arr)

{

cout << val << " ";

}

system("pause");

return 0;

}

 

输出结果:

1 3 5 7 9

}

示例4:

{

#include

#include

#include

#include

using namespace std;

int main()

{

map arr;

arr.insert(pair(1, "hello"));

arr.insert(pair(2, "world."));

for (auto &val : arr)

{

cout << val.first << "," << val.second << endl;

}

system("pause");

return 0;

}

 

输出结果:

1,hello

2,world.

}

}

}

}

3】列表初始化

{

(1)支持采用{}花括号来初始化自定义的类型,称为:列表初始化

{

map _map{{1,"hello"},{2,"wrold."}}; //== map _map = {{1,"hello"},{2,"wrold."}};

list _list{1,2,3,4,5};

set _set = {"li","xiao","gang"};

}

}

4】强枚举类型

{

(1)语法格式:

{

enum class 类型名 {枚举值表};

如:enum class People{yellow,black,white};

//这样就成功的定义了一个强类型的枚举People。

注意:等价于 enum struct 类型名{枚举值表}; (enum class中的成员没有公有私有之分,也不会使用模板来支持泛化的功能)

}

(2)强类型的枚举优点

{

(1)强作用域:强类型的枚举成员的名称不会被输出到其父作用域空间;

(2)转换限制:强类型枚举成员的值不可以与int隐式的相互转换。

(3)可以指定底层类型。强类型枚举底层类型为int,但是也可以显示的指定底层类型。具体方法:在enum名称后面加:“:type”,其中type可以是除wchar_t以外的任何int,如下:

enum class People:type char{yellow,black,white};

//指定了People为基于char的强枚举类型。

}

(3)对enum类型进行了扩展

{

(1)对于底层的基本类型;这个参考前面的“强类型的优点(3)

(2)对于其作用域的扩展。在c++11中,枚举成员的名字除了会自动输出到父作用域,也可以在枚举类型定义的作用域内有效。

 (3)若声明 了一个匿名的强枚举类型和示例,我们是无法对其示例进行设置值或去进行比较的,因此官方建议:在使用enum class(强枚举类型)的时候,应该总是提供一个名字。

}

}

5】新特性之 “=default” “=delete”

{

(1)=default 和=delete 概述

{

=default、=delete 是C++11的新特性,分别为:显式缺省(告知编译器生成函数默认的缺省版本)和显式删除(告知编译器不生成函数默认的缺省版本)。C++11中引进这两种新特性的目的是为了增强对“类默认函数的控制”,从而让程序员更加精准地去控制默认版本的函数。

}

(2)类的默认函数:

{

若不显著写明,则类会默认为我们提供如下几个函数:

(1)构造函数

(2)析构函数

(3)拷贝构造函数

(4)拷贝赋值函数(operator=)

(5)移动构造函数

以及全局的默认操作符函数:

(1)operator,

(2)operator &

(3)operator &&

(4)operator *

(5)operator->

(6)operator->*

(7)operator new

(8)operator delete

若我们在类中实现了这些版本之后,编译器便不会生成其对应的默认函数版本,这时需要我们显式的写上其对应的默认函数版本,

这样可能让编译器失去对这样的数据类型的优化功能。这是我们不希望看到的。因此最后使用 = default来修饰默认构造函数。

}

(3) 使用“=delete”来限制函数生成

{

我们经常需要控制某些函数的生成。在C++11之前,我们经常的普遍做法是将其声明为类的 private 成员函数,这样若在类外这些这些函数的操作时候,编译器便会报错,从而达到效果。但是这样做存在两个缺陷:

(1)不是最简化

(2)对于友元支持不友好

更为简洁直观的方法是使用: =delete

注:若缺省版本被删除了,重载该函数是非法的

}

示例:

{

#include

using namespace std;

class Student

{

   public:

    Student() = default;

    Student(const int a,const int b)

        :m_a(a)

        ,m_b(b)

    {

    }

    int getA()const{return m_a;}

    int getB()const{return m_b;}

    Student(const Student& ) = delete;

    Student& operator=(const Student& );

   private:

    int m_a;

    int m_b;

};

 Student& Student::operator =(const Student& ) = delete;

int main(int argc,char **argv)

{

    Student stu(1,2);

    cout<

    cout<

    Student stu1(3,4);

    stu1 = stu;

//编译报错:Student.cpp:42:10: error: use of deleted function ‘Student& Student::operator=(const Student&)’

    std::cout<::value<

    return 0;

}

 

}

}

6std::function

{

(1)std::function 概述:

std::function 是一种通用的多态函数包装器 。std::function 的实例可以存储、复制和调用任何可调用的目标——函数、lambda表达式、绑定表达式或其他函数对象,以及指向成员函数的指针和指向数据成员的指针(统称为“可调用对象”)。 存储的可调用对象 称为 std::function 的目标。如果一个std::function 不包含目标,它就被称为空。调用空std::function的目标会导致std::bad_function_call异常被抛出。

(2)std::function 存在的意义

对于每一个可调用的对象,它都有自己的类型;很多时候,多个不同类型的可调用对象却拥有着相同的调用形式。在C++98中,通用方法是定义一个指向该“类型”的函数指针,以存放其函数的地址,并放在一个函数表中,在需要调用的地方,直接通过指针来进行使用。然而,此方法是有局限的,因为它要求“可调用对象”的类型相同才可。

而std::function的出现解决了此棘手问题。因为std::function 是一个模板,实现了泛型化操作,所以即使“可调用对象”的类型不同也是可以的,只要保证其“调用形式”一样,即“函数返回类型、函数参数类型与函数参数个数相同”即可。

调用一个空的std::function实例会抛出std::bad_function_call异常

(3)std::function 操作

{

示例1:函数指针形式

{

typedef int (*pTr)(int,int);

//具体执行函数

int Func(const int a,const int b, pTr p)

{

    ….

 }

//加法

int add(int a,int b)

{

    return a+b;

}

//重载了函数调用的函数对象

struct Sub

{

    int operator()(int a,int b)

    {

        return a-b;

    }

};

int main()

{

    int a = 1,b = 2;

    std::cout<<"result: "<

    //std::cout<<"result: "<Sub());// error: cannot convert 'Sub' to 'pTr {aka int (*)(int, int)}' for argument '3' to 'int Func(int, int, pTr)'

    return 0;

}

}

示例2std::function()

{

由于std::function是一个模板,因此在实例化std::function类型的时候,需要显示的表名其对象的调用形式,即在std::function后面追加一对尖括号"<>"来指定类型。如:std::function 。声明了该std::function类型能够接收2个int类型参数且返回int类型的可调用对象。

//加法

int add(int a,int b)

{

    return a+b;

}

//重载了函数调用的函数对象

struct Sub

{

    int operator()(int a,int b)

    {

        return a-b;

    }

};

//除法

class Div

{

public:

    static int div(const int a, const int b)

    {

        assert(b);

        return a/b;

    }

};

//乘法操作

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

int main()

{

    int a = 4, b = 2;

    std::function f1 = add;       //普通函数

    std::function f2 = Sub();     //重载了函数调用运算符的函数对象

    std::function f3 = Lam;       //lambada表达式

    std::function f4 = Div::div;  //类静态成员函数

    std::cout<<"f1:"<

    return 0;

}

}

}

}

7decltype

{

 

}

8】auto

{

(1)对于函数,auto不能是形参的类型。

{

int printData(auto a)                //auto作为形参类型,编译报错

{

return ++a;

}

}

(2)对于结构体,非静态成员变量的类型不能是auto类型。

(3)不能声明auto数组:auto arr[10];            //编译报错

(4)实例化模板的时候,使用auto作为模板参数:template           //编译报错

}

你可能感兴趣的:(C++知识库,c++)