c++ 11 新特性讲解大全

链接:https://pan.baidu.com/s/1MnQUeOdDv7lTb4NbbN1S4w 
提取码:q0qw

1. 指针空值(0,NULL,nullptr)

初始化指针的时候一般将其设置NULL(或者0),NULL是一个宏定义。

#undef NULL
#if defined(__cplusplus)
#define NULL 0
#else
#define NULL ((void*)0)
#endif

NULL因其字面常量0的二义性,在c++98,字面常量0的类型既可以是整型,也可以是一个无类型指针(void *)。

在某些场景(例如,函数重载)的使用会遇到麻烦,C++11 引入关键字nullptr来避免二义性,nullptr是一个”指针空值常量”,仅可以被隐式转换为指针类型

所以以后指针初始化赋值就使用nullptr. 如int *p = nullptr;

nullptr做参数可以成功调用char*类型做参数的函数。

nullptr是一个编译期的常量

int n1 = nullptr; // 编译不过,报error: cannot initialize a variable of type 'int' with an rvalue of type 'nullptr_t'。
    int n2 = (int)nullptr; // 编译不过,即使强转也不行,报error: cast from pointer to smaller type 'int' loses information。
    int n3 = reinterpret_cast(nullptr);// 编译不过,

2. nullptr 与nullptr_t

nullptr 是一个指针空值常量, nullptr_t是一个指针空值类型,一个是常量,一个是类型。

typedef decltype(nullptr) nullptr_t;

c++ 11 新特性讲解大全_第1张图片

 3.= default

声明了带参数的构造函数版本,则必须声明不带参数的版本以完成无参的变量初始化,而这会导致对应的类不再是POD的(非POD类型,意味着编译器失去了优化这样简单的数据类型的可能),通过=default 可以使其重新成为POD类型。

什么是pod类型见:什么是 POD 数据类型_baidu_16370559的博客-CSDN博客

class D{
    public:
        D() = default;

        D(int i)
            :data(i)
        {}
    private:
        int data;
};

4.= delete

在以前,程序员若希望限制一些默认函数的生成,例如,单件类的实现需要阻止其生成拷贝构造函数和拷贝赋值函数,可以将拷贝构造函数和拷贝赋值函数声明为private成员,并且不提供函数实现。 见effective c++ 条款5。另外 C++11 标准给出了非常简洁的方法,即在函数的定义或者声明加上”= delete“。

class NoCopyConstructor2
{
    public:
        NoCopyConstructor2() = default;

        NoCopyConstructor2(int i)
            :data(i)
        {
        }
        NoCopyConstructor2(const NoCopyConstructor2&) = delete;

    private:
        int data;
};

5.左值右值

  • 左值:可以取地址并且有名字的东西就是左值。或者说可以放到等号左边的东西叫左值。

  • 右值:不能取地址的没有名字的东西就是右值。或者说不可以放到等号左边的东西就叫右值。

  • 纯右值:运算表达式产生的临时变量、不和对象关联的原始字面量、非引用返回的临时变量、lambda表达式等都是纯右值。

  • 将亡值:可以理解为即将要销毁的值。

  • 左值引用:对左值进行引用的类型。

  • 右值引用:对右值进行引用的类型。

  • 移动语义:转移资源所有权,类似于转让或者资源窃取的意思,对于那块资源,转为自己所拥有,别人不再拥有也不会再使用。

  • 完美转发:可以写一个接受任意实参的函数模板,并转发到其它函数,目标函数会收到与转发函数完全相同的实参。

  • 返回值优化:当函数需要返回一个对象实例时候,就会创建一个临时对象并通过复制构造函数将目标对象复制到临时对象,这里有复制构造函数和析构函数会被多余的调用到,有代价,而通过返回值优化,C++标准允许省略调用这些复制构造函数。

  • 具体见c++ 11 新特性之 左值右值_baidu_16370559的博客-CSDN博客

6. auto & decltype

auto:

让编译器在编译器就推导出变量的类型,可以通过=右边的类型推导出变量的类型。

  • auto的使用必须马上初始化,否则无法推导出类型

  • auto在一行定义多个变量时,各个变量的推导不能产生二义性,否则编译失败

  • auto不能用作函数参数,具体变现为不能当形参。

  • 在结构体中,auto不能用作非静态成员变量

  • auto不能定义数组,可以定义指针

  • auto无法推导出模板参数。如 vector v ={1};编译不通过

  • 在c++14,auto可以作为函数返回值推导

  • auto可以定义函数指针和Lambda表达式

void newc11::add(int a, int b)
{
	int c = a + b;
}

    auto a = 10; // 10是int型,可以自动推导出a是int
	auto au = 1;
	auto *pau = &forword;
	auto lambda = [au](int a) -> int {return a + au; };
	lambda(1);
	auto pfunc = add;
	pfunc(1, 2);

c++ 11 新特性讲解大全_第2张图片

 以前正常情况是声明迭代器

std::vector::iterator i;

decltype:

相对于auto用于推导变量类型,而decltype则用于推导表达式类型,这里只用于编译器分析表达式的类型,表达式实际不会进行运算。

decltype:总是以一个普通的表达式为参数,返回该表达式的类型。

decltype的语法形式为:decltype(e),这里e是一个表达式,而decltype(e)是一个类型指示符。decltype的结果不是值,而是一个类型

    int z ;
    float x;
    decltype (x + z) v;
    std::cout << typeid(v).name() << std::endl;     //打印出 float

c++ 11 新特性讲解大全_第3张图片

基于范围的for循环

对于知道范围的数组和stl容器,C++11提供了for迭代访问能力,结合auto,可简化代码。需要满足两个条件:

1)迭代的对象实现++和==操作;

2)知道范围

如vector,array,list ,set,map等容器都可以使用基于范围的for循环

for (declaration : expression)
{
    //循环体
}
declaration表示遍历声明,在遍历过程中,当前被遍历到的元素会被存储到声明的变量中。是迭代的变量

expression是要遍历的对象,它可以是表达式,容器,数组,初始化列表等。

    double price[5] = { 1.1,1.2,1.3,1.4,1.5 };
    for (auto& ii : price)  //在这里使用了引用,这样就不会拷贝,效率更高,还能修改元素的值
    {
        double a = ii;
    }

   for (auto ii : price)  
    {
        double a = ii;
    }

   for ( const auto& ii : price)   //既提高效率,也不修改值
    {
        double a = ii;
    }

7. 扩展了using的使用

不只用来声明命名空间了,using namespace xxx;

具体见:c++11中using的使用_baidu_16370559的博客-CSDN博客

8.列表初始化

声明一个以initializer_list 模板类为参数的构造函数,自定义的类可以使用列表初始化方式。

具体见:C++11列表初始化_baidu_16370559的博客-CSDN博客7. 

9.智能指针

C++98中,智能指针采用auto_ptr ,拷贝的时候返回的是一个左值,且不能调用delete[], C++11 已经废弃,改用如下三种智能指针:

  • unique_ptr:拥有管理内存的所有权,没有拷贝构造函数,只有移动构造函数,不能多个unique_ptr对象共享一段内存,可以自定义delete函数,从而支持delete [] 。
  • share_ptr:通过计数方式多个share_ptr可以共享一段内存,当计数为0的时候,所管理内存会被删除,可以自定义delete函数,从而支持delete[] 。
  • weak_ptr:观察shared_ptr管理的内存对象 ,只观察但不拥有。成员函数lock返回shared_ptr对象,若对应内存已经删除,则shared_ptr对象==nullptr,weak_ptr对象可以拷贝构造,拷贝构造出来的对象和原对象观察的是同一段内存。成员函数reset可以解除对内存的观察,注意,是解除观察,并不会删除对应内存对象。可以避免因shared_ptr的循环引用而引起的内存泄露。

具体见:c++ 智能指针auto_ptr (c++98)、shared_ptr(c++ 11)、unique_ptr(c++ 11)、weak_ptr(c++ 11)_baidu_16370559的博客-CSDN博客

10.lambda

lambda被用来表示一种匿名函数,这种匿名函数代表了一种所谓的lambda calculus。以lambda概念为基础的”函数式编程“是与命令式编程、面向对象编程等并列的一种编程范型。

[capture](parameters)mutable ->return-type{statement}

具体见:c++ 11 之lambda_baidu_16370559的博客-CSDN博客

11. #pragma once

保证头文件只会被include一次,用以代替先前的两种写法,_Pragma是一个操作符,可以用在宏定义中,比预处理指令灵活。

1)#pragma once 
2)#ifndef THIS_HEADER
  #define THIS_HEADER
  #endif

12.变长参数的宏定义

是指在宏定义中参数列表的最后一个参数为省略号,预定义宏__VA_ARGS__可以在宏定义的实现部分替换省略号所代表的字符串。

#define LOG(format, ...) fprintf(stdout, format, __VA_ARGS__)

比如调试时输出调试信息,正式发布时则不输出,可以这样

#ifdef DEBUG
#define LOG(format, ...) fprintf(stdout, ">> "format"\n", ##__VA_ARGS__)
#else
#define LOG(format, ...)
#endif

在调试环境下,LOG宏是一个变参输出宏,以自定义的格式输出;

在发布环境下,LOG宏是一个空宏,不做任何事情。

13.char、wchar_t互换。

具体见:c++ char、wchar_t互换_baidu_16370559的博客-CSDN博客

14. extern "C "和__cplusplus 关键字

为了实现C++与C兼容的,extern “C”是用来在C++程序中声明或定义一个C的符号。 extern "C"可以抑制C++对函数名、变量名等符号进行名称重整。

__cplusplus通常被定义为一个整数值,C++03标准中,其值是199711L, 在C++11中是201103L,

#ifdef __cplusplus
extern "C" {
#endif
 
void *memset(void* ,int , size_t);//函数声明
 
#ifdef __cplusplus
}
#endif

 C++支持函数重载,而C不支持,两者的编译规则也不一样。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为: void foo( int x, int y ); 该函数被C编译器编译后在符号库中的名字可能为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不 同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的

15.静态断言static_assert

即在预处理阶段做一些断言,静态断言的表达式结果必须是在编译期可以计算的表达式,可以放在任何名字空间,包括函数外部。assert只能用于运行时,且只能放在函数内部。

具体见:C++11 static_assert 使用方法及assert 与 #error_baidu_16370559的博客-CSDN博客

16.用操作符noexcept替代原先的throw 

表示其修饰的函数不会抛出异常

void excpt_func() noexcept(常量表达式)

具体见:C++11 关键字noexcept_baidu_16370559的博客-CSDN博客

17.快速初始化成员变量

  • 非常量的静态成员变量,C++98 和C++11 保持一致,都需要到类的头文件以外去定义,保证编译时非常量的类静态成员的定义最后只存在于一个目标文件中。
  • 非静态的成员变量,C++98只能在初始化列表中进行初始化,C++ 11增加支持就地初始化(用{}方式,不能用()方式),若同时对一个非静态的成员变量进行就地初始化和初始化列表初始化,最后起作用的是初始化列表(即后起作用)。
  • 常量的静态成员变量,C++98和C++11都支持就地初始化,C++98只支持整型或者枚举类型
  • c++ 11 支持使用初始化列表(即()),()只能使用在cpp文件中。还支持使用等号(即=)或者花括号(即{ })进行就地非静态成员变量的初始化,可以使用在头文件、源文件。

    struct MyStruct
    {
        const static int a{ 1 };
        int b = 1;
        int c{ 1 };
    };

18. C++98, sizeof 只能对实例的变量或者类的静态成员进行操作,不能对类的非静态成员进行操作

若要想达成对类的非静态成员的操作,可以用如下ugly方式, 0强转成对象的指针,并解析访问对应非静态成员变量。C++11之后,可以对类的非静态成员进行操作,无需此ugly方式。

#include 		
		
using namespace std;		
		
struct People{		
    public:		
        int hand;		
        static People* all;		
};		
		
int main(){		
    People p;		
    cout << sizeof(p.hand) << endl;		
		
    cout << sizeof(People::all) << endl;		
		
    cout << sizeof(((People*)(0))->hand) <

 19.C++11之前,声明友元类需要加上class关键字

class A;

class B{

friend class A;

private:

int b;

}

友元类的特点:可以无视类的成员的属性,即无论是public、protected或者是private都可以访问。

在这里A是B的友元类,所以A可以访问 B类私有成员b.

C++11进行了改进,不需要加上关键字class,这样就可以为类模板声明友元了

template

class people

{

      friend T; // T可以不是class,这种情况下会忽略友元定义

}

20. final,给程序员一种中途终止派生的能力

c++ final_baidu_16370559的博客-CSDN博客

21.C++98支持类模板的默认模板参数,不支持函数模板的默认模板参数。

C++11可以支持函数模板的默认模板参数。

类模板的默认模板参数需要从右到左依次;

函数模板的默认模板参数没有此约束。但如果是默认参数值还是要由右到左

函数模板

template 
void funt1(T1 t1, T2 t2 = "bbbb" ) {
	cout << t1 << i<< t2 << endl;
}

funt1("a");

类模板

template 
class ctem
{
public:
	T1 t1;
	void fun1(T2 t2);
};

template 
void ctem::fun1(T2 t2)
{

}
ctem<> tem;
tem.fun1("q");

//1、普通函数带默认参数,c++98 编译通过,c++11 编译通过
void DefParm(int m = 3) {}

//2、类模板是支持默认的模板参数,c++98 编译通过,c++11 编译通过
template 
class DefClass {};

//3、函数模板的默认模板参数, c++98 - 编译失败,c++11 - 编译通过
template 
void DefTempParm() {}

// 类模板的默认模板参数必须从右往左定义,函数模板的默认模板参数则没这个限定
template class DefClass1;
template class DefClass2;   // 无法通过编译

template class DefClass3;
template class DefClass4;         // 无法通过编译

template void DefFunc1(T1 a, T2 b);
template void DefFunc2(T a);

另外关于模板的介绍见:C++中 模板Template的使用_baidu_16370559的博客-CSDN博客

22.外部模板

extern 显示实例化一个模板函数(类),可以避免在多个编译单元都重复生成模板函数(类)的实例化代码,虽然在链接的时候会判断代码相同而做合并,但是会增加开销,有了外部模板,则可以大大缓解此问题。

具体见:C++11 外部模板_baidu_16370559的博客-CSDN博客

23.C++11 局部和匿名类型作模板实参

局部的类型、匿名的类型在C++98中不能作为模板类的实参,C++11已放开此限制。不过,匿名类型不能直接写在模板类的参数类型中,必须用独立的表达式语句。

struct {} a2; // 匿名类型变量

struct S3 {} a3; // 局部类型

c++ 11 新特性讲解大全_第4张图片

24.委派构造函数

C++11中,一个构造函数可以委派其他构造函数完成对象的构造。C++98中,需要将公共逻辑提取成一个公共私有函数,然后被多个构造函数调用。

特点:委派构造函数不能有初始化列表。所以初始化就只能在构造函数体内实现。

因为:在c++中,构造函数不能同时使用委派构造函数和初始化列表。

class Info
{
public:
	Info() {
		m_a = 1;
	}
	Info(int b) :Info() {
		m_b = b;
	}
	int m_a;
	int  m_b;
};


Info f0(1);
class DCExcept{			
    public:			
        DCExcept(double d)			
            try : DCExcept(1, d){			
                cout << "run the body." << endl;			
            }catch(...){			
                cout << "caught exception."<

25.非受限联合体Union

在c++98中的要求:

1.所有成员必须是POD类型的;

2.不允许拥有静态或引用类型的成员;

什么是pod类型见:什么是 POD 数据类型_baidu_16370559的博客-CSDN博客
C++11放开了限制,任何非引用类型都可以成为联合体的数据成员,也就是非受限联合体(Unrestricted Union)。

可以有静态成员函数(作用就是为了返回一个常量),但是不允许静态数据成员(否则所有该类型的联合体将共享一个值)。

另外:

1.如果非受限的联合体有非POD类型成员,而该pod成员拥有非平凡的构造函数,那么非受限的联合体成员的默认构造函数被编译器删掉。解决办法是:自定义非受限联合体的构造函数,使用到placement new。

c++ 11 新特性讲解大全_第5张图片

class cbase 
{
public:
	int m_aaa = 1;
	int m_b{2};
	int *m_ptr{nullptr};
	std::vector vecs{ "a","b" };
	cbase();
	cbase(int a);
	virtual ~cbase();
};

union MyUnion
{
	cbase ba;
	int m_a;
	float b;
	MyUnion() {
		new (&ba) cbase;
	}
	~MyUnion() {
		ba.~cbase();
	}
};

测试过程有出入,有静态数据成员也可以编译通过

延伸:

C++98标准规定,联合体会自动对未在初始化成员列表中出现的成员赋默认初值。//应该是以最长的结构为准。

在C++11标准中,当联合体中有一个非POD的成员,并且该非POD成员有非平凡的构造函数,那么这个联合体的默认构造函数就会被编译器删除(这样会导致构造失败),同理,其他的特殊成员函数比如默认拷贝构造函数,拷贝赋值构造函数及析构函数,也遵循这个标准。正是因为这个标准,可能有些非POD的成员无法构造成功,因此编译就失败。解决办法是:由程序员自己为非受限联合体定义构造函数。

26.枚举类(强类型枚举)

1.在C++98中,枚举值没有类或者命名空间的约束,两个枚举类型的枚举值若是一样,则会产生冲突。在C++11中,采用类名来约束,从而避免了冲突。

在C++98中,枚举值和整型可以隐式转换,在C++11中,必须显示转换。

在C++98中,底层存储大小不确定,在C++11中,可以指定底层存储类型。

具体表现:C++11强类型枚举——枚举类_baidu_16370559的博客-CSDN博客

27.C++11新标准为了做到最小的垃圾回收支持

首先对“安全”的指针进行了定义,安全派生的指针是指向由new分配的对象或其子对象的指针。

具体见:C++11与最小垃圾回收_baidu_16370559的博客-CSDN博客

28. 常量表达式

  • constexpr:编译时常量,在编译期就可以算出来。
  • const  : const并未区分出编译期常量和运行期常量,它限定一个变量不允许被改(但这个东西仍然可能是个动态变量,如做函数参数)。在大多数情况下,const描述的是运行时常量性

const 很明确只有「只读」一个语义,不会混淆。C++ 在此基础上增加了「常量」语义,也由 const 关键字来承担,引出来一些奇怪的问题。C++11 把「常量」语义拆出来,交给新引入的 constexpr 关键字

constexpr int getconst() {
    return 2;
};

void cofunc() {
    int arr[getconst()] = {0};
};

29. std::function

讲std::function前首先需要了解下什么是可调用对象

满足以下条件之一就可称为可调用对象:

  • 是一个函数指针

  • 是一个具有operator()成员函数的类对象(传说中的仿函数),lambda表达式

  • 是一个可被转换为函数指针的类对象

  • 是一个类成员(函数)指针

  • bind表达式或其它函数对象

https://blog.csdn.net/baidu_16370559/article/details/123096096

30.std::bind

std::bind是函数模板(是一个函数)

使用std::bind可以将可调用对象和参数一起绑定,绑定后的结果使用std::function进行保存,并延迟调用到任何我们需要的时候。

std::bind_baidu_16370559的博客-CSDN博客

31.关键字override 

 override, 明确告知这个函数是继承、重载的,避免以前写法的混淆。这两个关键字用于函数后面,才会产生此效果。具体见:在虚函数 声明中写override的作用_baidu_16370559的博客-CSDN博客

32.继承基类构造函数  

将基类T中的系列构造函数传递到派生类U中。前提是:基类的构造函数是public.注意:继承构造函数只能初始化基类中的数据成员,对于派生类中的数据成员,仍然需要自行处理。

 using T::T;  //using声明基类T的构造函数

具体见:c++11中using的使用_baidu_16370559的博客-CSDN博客

33.pod 类型

具体见:什么是 POD 数据类型_小飞侠hello的博客-CSDN博客_podac分别什么意思

34.内联命名空间

C++11中引入了内联命名空间(inline namespace)

1、它的特点就是不需要使用using语句就可以直接在外层命名空间使用该命名空间内部的内容,而且无需使用命名空间前缀。

namespace Parent
{
 
    inline namespace Child1
    {
        struct child1_data{int a;} ;
    }
    inline namespace Child2
    {
        struct child2_data{int b;} ;
    }
    namespace child3
    {
        child1_data data1;   
        child2_data data2;   
    }
}

2.允许在父名字空间定义或 特化子名字空间的模板

namespace NameA {
    inline namespace NameB {                            // 声明为inline
        struct Example {};
    }

    inline namespace NameC {                            // 声明为inline
        template class Class {};
        Example exC;                                    // 不用打开名字空间NameB也能使用
    }
}

namespace NameA {
    template<> class Class {};                // 使用内联名字空间还能在不打开名字空间的情况下,进行模板特化(否则会编译失败)
}

using namespace NameA;
int main()
{
    Example ex;                                         // 不用打开名字空间NameB也能使用
    Class classEx;
    return 0;
}

35.模板的别名

template
using ptr=std::shared_ptr;
 
//这里模板定义ptr为智能指针std::shared_ptr定义的别名


#include
#include
using namespace std;
template
using ptr=std::shared_ptr;
 
int main()
{
    //这里借助模板别名,用ptr 来代替std::shared_ptr
    ptr p=std::make_shared();
    return 0;
}

36.追踪返回类型

auto dev(int a, char*b) ->int
{
	return a;
}

template
auto add(T1 t1, T2 t2) ->decltype(t1 +t2) {
	return (t1 + t2);
}

auto ad = add(1, 1.2);

复合符号-> decltype(t1 + t2) 被称为追踪返回类型。此外这个要结合auto 和return_type 一起使用。与普通函数不同,追踪返回类型是返回类型后置。

37.类与默认函数

具体见:c++ 类与默认函数、包括构造函数和析构函数的特点_小飞侠hello的博客-CSDN博客

38.原子操作和原子类型

atomic_bool bret = true;//还有很多内置类型
atomic   t;
如:atomic m_base{1};   //自定义类型

atomic模板类的拷贝构造函数、移动构造函数、operator=等总是默认被删掉的。

 atomic类型的变量来构造其木星参数类型T的变量则是可以的。

39.alignof和alignas

操作符alignof表示一个定义完整的自定义类型或者内置类型或者变量,返回的是一个std::size_t类型的整型常量。如sizeof操作符一样。

如:

struct  MyStructss
{
	int a;
	int b;
};
int asize = alignof(MyStructss);   //asize = 4

对齐描述符alignas

既可以接受常量表达式,也可以接受类型作为参数。主要修饰各种变量、类的数据成员

使用常量表达式,必须是以2韦自然数幂次作为对齐数。

alignas不可以修饰函数

alignas (double) char  cc;
alignas (alignof(double)) char  cd;

你可能感兴趣的:(c++,c++,新特性,c++,c++11)