C++11 新特性 学习笔记(1)

来源:互联网 发布:office mac 2011 密钥 编辑:程序博客网 时间:2024/05/18 01:40

C++11新特性

 

_Pragma操作符和#pragma预处理指令的区别

#pragma once相当于_Pragma(“once”)//括号里为字符串常量

区别:_Pragma可以在宏定义中展开,而#pragma不能展开

C++11整型的最大改变就是多了long long(至少64位)

C++11中只定义了五种标准的有符号整形:signed char; short int; int; long int; long longint;每种有符号整型都有一种对应的无符号整数版本,且有相同的存储空间。有些编译器会自行扩展一些整型。如UINT_16等等。

noexcept修饰符与noexcept操作符

C++11中用noexcept替换throw()异常处理。 noexcept(true) 表示不抛出异常,如有异常,则noexcept自动调用std::terminate中断程序运行;noexcept(false) 表示抛出异常。noexcept相当于noexcept(true)。noexcept可以有效地阻止异常的传播与扩散。noexcept操作符:noexcept(T());其中T表示一个模板,当T()表达式有可能抛出异常,noexcept(T())值为false,否则为true。

C++11标准中类的析构函数默认为noexcept(true),提高应用程序的安全性。如果程序员显示的为析构函数指定noexcept(false),析构函数就不会保持默认值。

C++11快速初始化成员变量

C++11标准中允许非静态成员变量的初始化有多种形式,除了初始化成员列表外,还允许使用等号=或者花括号{}进行就地初始化非静态成员变量。如:

struct Init{ Init (int i,double j):a(i),b(j){};int a = 1; double b {1.2}; };//{}的初始化类似于用圆括号()对自定义变量的表达式列表初始化。不能在类中使用()对变量进行初始化

就地初始化和初始化列表不会冲突。优先使用初始化列表提供的值,如果没有,就使用就地初始化提供的默认值。

对于非常量的静态成员变量,C++11与C++98一致。对于静态常量成员,除了可以用const关键字外,还可以用constexpr来对静态常量成员进行声明。

非静态成员的sizeof

struct People{

public:

       inthand;

static People *all;

};

int main() {

       Peoplep;

       sizeof(p.hand);              // C++98中通过, C++11中通过

       sizeof(People::all);        // C++98中通过, C++11中通过

       sizeof(People::hand);     // C++98中错误, C++11中通过。C++11无需定义实例来获得非静态成员变量的大小。

}     //在C++98中,只有静态成员,或者对象的实例才能对其成员进行sizeof操作。因此C++98中在没有定义类实例的时候,要获得类成员的大小,通常会采用以下代码:

sizeof(((People *)0)->hand);//先将0转化为People类指针,再获得其成员变量。

扩展的friend语法

class Poly;

typedef Poly  P;

class LiLei{friend class Poly;};     // C++98通过, C++11通过

class Jim{friend Poly;};               // C++98失败, C++11通过

class Tom{friend P;};                  // C++98失败, C++11通过

在C++11中,声明一个类为另外一个类的友元时,不再需要使用class关键字,甚至还可以使用别名。这点变化可以使程序员为类模板声明友元。如:

class P;

template <typename T> class People{friendT};

People<P> PP;       //类型P在这里是People类型的友元。

People<int> Pi;      //对于int类型的模板参数,友元声明被忽略。

 

final/override控制

final关键字:可以在派生过程中任意的阻止一个接口的可重载性。如:

struct Object{virtual void fun() = 0;};

struct Base : public Object { void fun()final;};//声明为final

struct Derived: public Base { void fun();};   //无法通过编译

虚函数描述符override:如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,否则将无法通过编译。

C++11支持函数模板的默认参数,而C++98不支持。如:

template <typename T = int> void func() {};      // C++98编译失败, C++11编译通过

与类模板不同的是,在为多个默认模板参数指定默认值的时候,必须遵循“从右往左”的规则进行指定,而对函数模板来说可以不必遵循此规则。如:

template <typename T1, typename T2= int >class Base;

template <typename T1 = int, typename T2>class Base1;   //无法通过编译

template <typename T1, int i=0 > classBase2;

template < int i=0, typename T2> classBase3;     //无法通过编译

template <typename T1 = int, typename T2>void fun1(T1 a, T2 b) ;

template < int i = 0, typename T1>  void fun2(T1 a) ;

外部模板

外部模板的使用可以减小模板实例化展开的开销,减少代码在编译和链接过程中产生的代码冗余,优化编译的时间和空间。在项目较大的情况下,建议使用。

对于模板:template < typename T>  void fun(T a) ;

我们只需声明:template void fun<int>( int);//显示地实例化,编译时会强制实例化出一个fun<int>( int)版本的函数。

外部模板声明:template void fun<int>( int);//编译时就会用已实例化的模板函数,不会再进行实例化。

外部模板声明不能用于静态函数,但可用于类静态成员函数,因为静态函数没有外部链接属性,不可能在本编译单元之外出现。

局部和匿名类型做模板实参

在C++98中,局部的类型和匿名的类型在C++98中不能做模板类的实参,在C++11中可以。如:

template < typename T>  class X{} ;

template < typename T>  void fun(T t) ;

struct A{ } a;  //普通全局结构体

struct {int i;}b;//匿名全局结构体,b是匿名类型变量

typedef struct {int i;}B;// B是匿名类型

void Fun(){

       structC{ } c; //局部结构体,C是局部类型

X<A> x1;// C++98通过, C++11通过

X<B> x2; //C++98错误, C++11通过

X<C> x3; //C++98错误, C++11通过

fun(a);    //C++98通过, C++11通过

fun(b);    //C++98错误, C++11通过

fun(c);    //C++98错误, C++11通过

}

在C++98中除匿名结构体外,匿名的联合体以及枚举类型也不能做模板参数。而在C++11中都是允许的。但不能把匿名结构体的直接声明放在模板实参位置,如:

fun< struct {int i;}> f;//error

初始化列表的初始化方式总是优先于构造函数完成(实际在编译完成时就已经决定了)。

左值、右值和右值引用

左值:可以取地址的,有名字的

右值:不能取名字的、没有名字的

右值分类:可分为将亡值和纯右值

纯右值:非引用返回的函数返回的临时变量值、不跟对象关联的字面量值、类型转换函数返回值、lambda表达式等

将亡值:是C++11新增的跟右值引用相关的表达式,这类表达式通常是将要被移动的对象。比如返回右值引用T&&的函数返回值、std::move的返回值或者转换为T&&的类型转换函数的返回值。而剩余的,可以标识函数、对象的值都属于左值、在C++11中所有的值必属于左值、将亡值、纯右值三者之一。

右值引用:C++11中,右值引用就是对一个右值进行引用的类型。事实上,由于右值通常不具有名字,我们只能通过引用的方式找到他的存在。如:

T && a = ReturnRvalue();//这个表达式中,假设ReturnRvalue返回一个右值,我们就声明了一个名字为a的右值引用,其值等于ReturnRvalue函数返回的临时变量的值。

左值引用(C++98中的引用)和右值引用:左值引用是具名变量值的别名,而右值引用则是不具名变量的别名。

一般来说,ReturnRvalue返回的右值在表达式语句结束后,其生命周期也就结束了,而通过右值引用的声明,该右值又“重获新生”,其生命周期将与右值引用类型变量a的生命周期一样。

常量左值引用在C++98中是“万能”的引用类型。它可以接受非常量左值、常量左值、右值对其进行初始化。

const bool & judgement = true;//常量左值引用绑定右值

const bool judgement = true;//这两个表达式的区别在于,从语法上讲,前者直接使用了右值并为其“续命”,而后者的右值表达式结束后就销毁了。

左值引用一般是常量左值引用,只读;右值引用可以是非常量的,可以修改里面的参数。同样可以减少临时变量拷贝的开销。不过改变一个临时变量值意义不大。

class Copyable{

public:

Copyable() : i ( new int (3) ) {}

~Copyable() { delete i; }

       Copyable( const Copyable &&o ):i( new int (*o.i) ){}

Copyable ( Copyable &&o ) noexcept : i(o.i){o.i = nullptr;}// 移动构造函数,通常声明为noexcept。

       int*i;

}

void AcceptVal (const Copyable &);//左值引用

void AcceptVal (Copyable &&);//右值引用,需要一个以右值引用为参数的移动构造函数

std::move:将左值强制转化为右值

 

 

列表初始化

int a[] = {1,3,5};    //C++98通过, C++11通过

int b[] {2,4,6};             //C++98失败, C++11通过

vector< int > c{1,3,5};  // C++98失败, C++11通过

map<int, float> d = {{1,1.0f},{2,2.1f}};     // C++98失败, C++11通过

初始化形式:

等号“=”加赋值表达式。int a=3+4

等号“=”加花括号式的初始化列表。int a={ 3+4 }

圆括号式的表达式列表。int a(3+4)

花括号式的表达式列表。int a{3+4}

后两种也可以用于new操作符:int *i=new int(1); int *i=new int{1};


原创粉丝点击