C++11新特性个人总结

目录

1  关键字

1.1  constexpr

1.2  explicit

1.3  auto

1.4  noexcept

1.5  nullptr

1.6  decltype

1.7  override

1.8 final

1.9  using

1.10  extern

1.11 sizeof

1.12 default

1.13 delete

1.14 static_assert

1.15 friend

1.16 alignof

1.17 alignas

1.18 thread_local

1.19 char16_t和char32_t

2 语法

2.1 for循环

2.2 lambda表达式

2.3 模板的新增功能

2.3.1 带缺省参数的模板

2.3.2 变长模板参数

2.3.3 模板类型自动推导

2.3.4 模板的嵌套

2.3.5 模板的特化(非新特性,复习用)

2.4 函数的变长参数

2.5 委托构造函数

2.6 右值引用

2.7 移动语义与完美转发

2.8 匿名类型

2.9 枚举

2.9.1 强枚举类型

2.9.2 枚举值指定数据类型

2.10 成员变量在定义时初始化

2.11 列表初始化变量

2.12 unicode字符串

2.13 内联命名空间

2.14 用户自定义字面量

3 属性

3.1 noreturn

3.2 carries_dependency

4 宏

4.1 __func__

4.2 __VA_ARGS__

结语


 

编译器版本:GCC 4.8.1、Clang 3.3

__cplusplus:201103L

1  关键字

1.1  constexpr

用于定义常量,在变量声明时必须初始化。如果变量作为类或结构体的成员变量,则变量必须声明为静态

例子1:

class A
{
private:
    static constexpr int num = 10;
};

例子2(与模板的组合使用):

/*
 * 经典案例:判断两个类型是否相同
 */

template<bool flag>
struct bool_struct
{
    static constexpr bool value = flag;
};

template<typename T, typename U>
struct IsSame : public bool_struct<false> {};

template<typename T>
struct IsSame<T, T> : public bool_struct<true> {};

int main( )
{
    bool same = IsSame<int, float>::value; //same值为false
    return 0;
}

 

1.2  explicit

用于构造函数的调用限制,表示禁止某个拷贝构造函数。

如:

class B
{
public:
    B() {}
    B(int) {}
}

class A
{
public:
    A() {}
    explicit A(int) {}
}

B class_b1 = B();//正确
B class_b2 = 10;//正确(此处即调用拷贝构造函数)
B class_b3;//正确
B class_b4(100);//正确
A class_a1 = A();//正确
A class_a2 = 20;//编译出错(此处即调用拷贝构造函数)
A class_a3;//正确
A class_a4(1000);//正确

1.3  auto

自动类型,定义变量的关键字,会在编译器自动转换为目标类型

如:

auto a = 10;//a为int类型
auto b = 20.0;//b为float类型
auto c = new int(30);//c为int *类型
auto d = A();//d为A类型
auto e = new A();//e为A*类型
char arr[10];
auto f = arr;//f为char[]类型

 

1.4  noexcept

语法:函数声明 noexcept(expression),其中expression为常量表达式,可为空,当为true时表示函数不会抛出任何异常,一旦发生异常,程序结束。否则表示可抛出异常。

如:

void foo() noexcept;//相当于声明foo()函数不会抛出异常

 

1.5  nullptr

表示空指针,专用于指针。条件nullptr == NULL结果为true

 

1.6  decltype

类型推导,获取表达式的返回结果的类型

如:

int i, j;
decltype(i + j) m;//因为i + j的结果是int类型,所以m为int类型

 

1.7  override

标记成员函数是重写(覆盖)的,用于类或结构体的成员函数后面,重写的基类的成员函数必须是虚函数。

如:

class A
{
public:
    virtual void foo() {}
    void func() {}
}

class B : public A
{
public:
    void foo() override {}
    void func() override {} //编译报错,基类的func函数不是虚函数
}

int main()
{
    B tmp;
    tmp.foo();//调用的class B的foo函数
}

 

1.8 final

指定类不可继承

如:

class MyClass final
{ }

class ChildClass : public MyClass  //编译报错,不能继承MyClass
{ }

 

1.9  using

新增类型重命名功能,即同typedef;新增带模板的类型别名,typedef无此功能

例子1:

using uint8 = unsigned char;

例子2:

template<typename T1, typename T2>
struct MyStruct { };

template<typename T>
using NewType1 = MyStruct<T, int>;

template<typename T>
using NewType2 = MyStruct<T, T>;

template<typename T>
using NewType3 = MyStruct<float, T>;

using NewType4 = MyStruct<double, int>;

 

1.10  extern

新增引入外部模板功能

如:

extern template class vector<MyClass>;

 

1.11 sizeof

新增计算模板类型参数个数的功能,用于变长模板参数

如:

template<typename...T>
struct MyStruct
{
    unsigned long get_arg_num(T...args) { return sizeof...(args); } //注意三个点,此处返回参数个数
}

 

1.12 default

新增指定默认构造函数的功能

如:

class MyClass
{
public:
    MyClass(int i) = default;
}

1.13 delete

新增指定函数为禁用的功能,调用禁用函数会报编译错误

如:

class MyClass
{
public:
    void foo() = delete;
}

 

1.14 static_assert

静态断言,在编译期时的断言机制。用法同assert,但是static_assert断言的变量需是能在编译期计算出来的变量。不会生成任何代码,因此不会造成任何性能上的损失。

如:

template<bool flag>
void foo()
{
    static_assert(flag, "ok");
}

int main( )
{
    foo<true>();//当判断为true时,断言通过,正常编译
    foo<false>();//当判断为false,会产生编译错误
    return 0;
}

 

1.15 friend

新增支持模板

如:

template<typename T>
class A{
public:
    friend T; //指定类的友元类,允许友元类访问本地参数
private:
    int n;
};

class B{
public:
    //相当于A<B>类型以B类型作为友元类,B类可以访问A<B>类的成员
    void func(A<B> *arg)
    {
        cout << arg->n << endl;
    }

};

 

1.16 alignof

获取类型的最小对齐字节数 ,前置知识:内存对齐

如:

struct MyStruct
{
    short a;
    int b;
    long long c;
};

int main()
{
    int a = sizeof(MyStruct);//16
    int b = alignof(MyStruct);//8
    int c = alignof(int);//4
    return 0;
}

1.17 alignas

设定最小对齐字节数,设置的数必须是2的倍数

如:

struct alignas(16) MyStruct
{
    short a;
    int b;
    long long c;
};

int main()
{
    int a = alignof(MyStruct); // 16
    int b = sizeof(MyStruct); // 16
    return 0;
}

 

1.18 thread_local

指定变量的声明周期为线程生命周期,即变量会在线程开始时生成,线程结束后释放

如:

thread_local int a = 10;
thread_local double b = 100.0;

1.19 char16_t和char32_t

基本数据类型,两字节字符与四字节字符,这里不展开了

2 语法

2.1 for循环

新增基于范围的遍历用法(数组同样适用)

如①:

//遍历数组的每一个元素
char c[10];
float f[10];
MyClass mc[10];

for(char ch : c)
{ }

for(float ff : f)
{ }

for(MyClass m : mc)
{ }

如②:

//遍历容器的所有元素
std::list<int> stl_list;
std::map<int, MyClass *> stl_map;
std::string str;//标准库字符串实现了迭代器

for(int tmp : stl_list)
{ }

for(std::pair<int, MyClass *> p : stl_map)
{ }

for(char ch : str)
{ }

 

如③:

//与auto关键字的组合使用
std::set<MyClass *> stl_set;

for(auto it : stl_set)   //此时auto代表MyClass *类型
{ }

 

2.2 lambda表达式

语法格式:[option] (形参列表) ->返回值类型 { 函数体 }

返回值:lambda表达式对象,用auto接收

其中,“->返回值类型”可省略不写,编译器自动推导返回值类型;“(形参列表)”可省略不写,表示无参。lambda表达式的生命周期与局部变量一样,但可以作为函数一样调用。

[option]捕获列表选项:用于捕获lambda表达式相对于“函数体”的外部变量,默认为按值捕获

 空 —— 不传递,不捕获任何“函数体”外的任何变量

 = —— 按值传递,捕获外部变量的值,只读限制,若尝试赋值会报错。默认为该传递方式

 & —— 按引用传递,捕获外部变量的引用

 

实例:

static long count = 0;

class MyClass
{
private:
    int num;
public:
    void set(int arg) { num = arg; }
    MyClass( )
    {
        num = 0;
        auto func1 = [this]( )
        {
            this->num = 20;
            cout << this->num << endl;
        };//捕获this指针,赋值成功
        num = 50;
        auto func2 = [&num ]( )  //报错,不能指定捕获非静态成员的引用
        {
            num = 80;
        };
        auto func3 = [&]( )
        {
            count = 2000;
            num = 100;
        }; //捕获func3所在的地方所能访问到的所有变量的引用,成功赋值
        func1( );//输出0
        func2( );//调用后,num的值为80,成功修改
        func3( );//调用后,count的值为2000,num的值为100,成功修改
    }
};

int main( )
{
    MyClass mc;
    double sum = 5;
    auto set_func = [=]( long arg ) { count = arg; };//可以使用全局变量count变量的值,但修改不了全局变量count的值
    set_func( 100 );//调用lambda表达式,此时全局变量count的值依然为0
    auto func1 = [=count]( ){ cout << count << endl; };//指定使用全局变量count的值
    auto func2 = [=mc]( ){ mc.set( 200 ); };//指定使用局部变量mc的对象
    auto func3 = [sum, count]( ) { cout << sum << endl; };//多个参数间用逗号隔开
    count = 1000;
    func1( ); //此时输出0,而不是输出1000,因为func1中count的值是原来传递进去的0
    func2( );//局部变量mc的成员num,其值依然为100
    func3( );//局部变量sum的值仍然为5

    return 0;
}

2.3 模板的新增功能

2.3.1 带缺省参数的模板

如:

template<typename T = int, T arg = 10>
void foo( )
{
    cout << arg << endl;
}

int main( )
{
    foo( );//此处输出10
    foo<long, 205>( );//此处输出205
    return 0;
}

2.3.2 变长模板参数

过于复杂,加上本人表达能力欠缺,此处有位博主给出了详细的解释,链接如下:

https://www.cnblogs.com/zenny-chen/archive/2013/02/03/2890917.html

2.3.3 模板类型自动推导

如:

template<typename T>
void foo( T arg )
{
    cout << arg << endl;
}

int main( )
{
    foo(10);//相当于foo<int>(10),编译器自动推导T为int类型
    foo(50.2);//相当于foo<double>(50.2),编译器自动推导T为double类型
    foo("abcdefg");//相当于foo<char *>("abcdefg"),编译器自动推导T为char *类型

    return 0;
}

 

2.3.4 模板的嵌套

新特性将右移运算符“>>”和模板嵌套进行了区分

如:

std::queue<std::list<int>> temp;

 

2.3.5 模板的特化(非新特性,复习用)

类似函数的重载,模板的特化,能保持模板类或模板函数的名称相同,并能达到扩充功能的效果。

注:最前面定义的模板属于主要的模板,后面的模板都属于主要模板的特化

例子1:

template<typename T, typename U>
struct Template {};//该模板属于主要模板

template<typename T>
struct Template<T, T> {};//该模板属于特化模板

int main()
{
    Template<int, int> a;//此处使用的模板类为下面的Template
    Template<int, float> b;//此处使用的模板类为上面的Template
    return 0;
}

 

例子2:

/*
 * 例子:去除类型引用
 */

//主要模板
template<typename T>
struct RemoveReference
{
    typedef T type;
};

//特化模板,获取指针类型的原类型,即去除指针
template<typename T>
struct RemoveReference<T*>
{
    typedef T type;
};

//特化模板,获取引用类型的原类型,即去除引用
template<typename T>
struct RemoveReference<T&>
{
    typedef T type;
};

//特化模板,获取右值引用类型的原类型,即去除右值引用
template<typename T>
struct RemoveReference<T&&>
{
    typedef T type;
};

int main( )
{
    int *a = new int(10);//a属于整型指针类型
    RemoveReference<int *>::type b = 10;//去除int *的指针类型,获取int类型,因此b属于整型
    return 0;
}

 

2.4 函数的变长参数

新增使用std::initialize_list模板类来接收变长参数,结合for循环可遍历每一个参数

如:

void foo( std::initializer_list<int> arg )
{
    for( int tmp : arg )
    {
        cout << tmp << endl;
    }
}

void func( std::initializer_list<int> arg1, std::initializer_list<long> arg2 )
{
    for( int tmp : arg1 )
    {
        cout << tmp << endl;
    }

    for( long tmp : arg2 )
    {
        cout << tmp << endl;
    }
}



int main( )
{
    foo({ 10, 20, 60, 700 });//std::initialize_list接收花括号列举的变量
    func({ 10, 20, 30, 40 }, { 100, 200, 300, 400, 500 });//特别之处,可传多个不同类型的变长参数
    return 0;
}

 

 

2.5 委托构造函数

解决了多个构造函数的代码相同的问题,在构造函数中调用另一个构造函数(可以是自己的构造函数,也可以是基类的构造函数)来达到执行相同代码的功能。但,一个构造函数只能同时调用一个另外的构造函数

如:

class A
{
protected:
    int num;
public:
    A(int arg) : num(arg) { }
    A() : A(10) { }
};

class B : public A
{
private:
    int value;
public:
    B(int arg1, int arg2) : A(arg1)
    {
        value = arg2;
    }
    B(int arg) : B(arg, arg) {}
    B() : B(0) {}
};

int main( )
{
    A a1;//成员num为10
    A a2(100);//成员num为100
    B b1;//成员num为0,成员value为0
    B b2(1);//成员num为1,成员value为1
    B b3(3, 5);//成员num为3,成员value为5
    return 0;
}

 

2.6 右值引用

右值——简单来说即是不可以使用取地址运算符&来获取地址的对象,或者没有变量名的对象都是右值。

左值——右值以外的对象(变量)都可视为左值

&10;

&int();

&MyClass();

其中,10、int()、MyClass()都是右值。10是一个常数,根本不存在于内存,int()会产生一个临时整型对象,MyClass()会产生一个临时的自定义类型对象,而临时对象的地址都是不可获取的。

 

右值引用——引用临时变量,达到避免对象复制而造成的性能损失

语法:&&

例子:

int && a = 10;  ①

double&& b = 20.0;  ②

MyClass &&c = MyClass();  ③

解释:

对于①②,a和b引用一个常量,编译器会给10和20.0各自分配一个临时内存,然后将变量引用该临时内存;对于③,由于MyClass()本身会产生一个临时变量,则将c引用该临时变量。此后,则跟普通的引用一模一样,当修改了c的值,临时变量的值也会改变,修改了a和b的值,其对应引用的临时内存的值也随之改变。效果相当于(当然不能写,只是效果相似):

int aa = 10;

double bb = 20.0;

MyClass cc();

 

int & a = aa;

double& b = bb;

MyClass &c = cc;

 

当然也可以用于函数的形参当中

如:

void foo(int && a)
{
    cout << a << endl;
}

int main()
{
    int num = 100;
    foo(10);
    foo(num);//编译出错,num不是右值
    return 0;
}

 

2.7 移动语义与完美转发

本质上都是强制类型转换

 

移动语义——将参数转变为右值引用

完美转发——将一个右值引用转发到另一个右值引用

底层实现:

template<typename T>
struct RemoveReference
{
    typedef T type;
};

template<typename T>
struct RemoveReference<T&>
{
    typedef T type;
};

template<typename T>
struct RemoveReference<T&&>
{
    typedef T type;
};

//完美转发
template<typename T>
constexpr typename RemoveReference<T>::type& forward(T & arg)
{
    return static_cast<typename RemoveReference<T>::type&>(arg);
}

//移动语义
template<typename T>
constexpr typename RemoveReference<T>::type&& move(T && arg)
{
    return static_cast<typename RemoveReference<T>::type&&>(arg);
}

 

应用举例:

 

struct MyStruct
{
    int num;
};

void foo2(const MyStruct &arg)
{
    std::cout << arg.num << std::endl;
}

void foo1(MyStruct && arg)
{
    //此处,arg是右值引用类型,但是也是一个“左值”,arg引用了一个临时变量
    foo2(forward(arg));
}

int main( )
{
    MyStruct temp;
    foo1(MyStruct());//本来就是右值,可直接传递
    foo1(move(MyStruct()));//本来就是右值,经过移动语义的转换,变成右值引用类型
    foo1(move(temp));//移动语义,将左值转换为右值引用类型
    return 0;
}

 

2.8 匿名类型

即一个类型没有名称,如下例,tmp对象是一个结构体类型,但类型名称不知道。甚至可以与普通的类型一样使用

例子1:

int main( )
{
    struct { int a; int b;} tmp;
    tmp.a = 10;
    tmp.b = 20;
    return 0;
}

例子2:

int main( )
{
    class
    {
    public:
        int a;
        int b;
    } tmp;

    tmp.a = 10;
    tmp.b = 20;

    return 0;
}

2.9 枚举

2.9.1 强枚举类型

即枚举类,添加class关键字,解决在多枚举情况下命名冲突的问题

如:

enum class Color
{
    blue,
    yellow
};

int main( )
{
    Color tmp = Color::blue;
    int num = (int)tmp;
    return 0;
}

 

2.9.2 枚举值指定数据类型

为枚举成员指定一个数据类型

例子1:

enum Color : long long
{
    blue,
    yellow
};

int main( )
{
    long long tmp = blue;
    return 0;
}

例子2(与枚举类组合使用):

enum class Color : long long
{
    blue,
    yellow
};

int main( )
{
    Color tmp = Color::blue;
    long long num = (long long)tmp;
    return 0;
}

 

2.10 成员变量在定义时初始化

在定义成员变量时,可直接对齐初始化

如:

class A{
private:
    int n = 100;
};

 

2.11 列表初始化变量

对变量进行初始化时,可使用花括号对其成员进行初始化,当然也会受到访问权限的影响。也可运用到成员变量,除静态变量

如:

class A{
private:
    int n = 100;
};

struct B
{
    float num1;
    long long num2;
};

int main( )
{
    B b1{100.0};//对num1成员赋值
    B b2{100.0, 20000};
    A a1{10};//编译错误,无访问权限
    return 0;
}

 

2.12 unicode字符串

新增支持unicode编码字符串,标准库还提供了不同编码字符串间的转换函数

如:

int main()
{
    const char* u8str = u8"1344";//utf8编码字符串,对应std::wstring
    const char16_t* u16str = u"dfada";//utf16编码字符串,对应std::u16string
    const char32_t* u32str = U"dfada";//utf32编码字符串,对应std::u32string
    return 0;
}

 

2.13 内联命名空间

直接上代码:

namespace Lib
{
    inline namespace Lib_1
    {
        template <typename T> class A; 
    }
    template <typename T> void g(T);
};

struct MyClass {  };

namespace Lib
{
    template<> class A<MyClass> {  };
};

int main()
{
    Lib::A<MyClass> a;
    g(a);  //编译通过
    return 0;
}

2.14 用户自定义字面量

该新特性允许用户将自定义类型可以像内置类型一样向函数传递字面常量,但需要重载“”_C运算符(两个双引号和下划线大写C)

如:

#include <cstdlib>
#include <iostream>
using namespace std;

typedef unsigned char byte;
struct RGBA
{
    byte r;
    byte g;
    byte b;
    byte a;
    RGBA(byte R, byte G, byte B, byte A = 0) : r(R), g(G), b(B), a(A) {}
};

RGBA operator ""_C(const char *col, size_t n)
{
    const char *p = col, *end = col + n;
    const char *r, *g, *b, *a;
    r = g = b = a = nullptr;
    for(; p != end; p++)
    {
        if(*p == 'r') r = p;
        else if(*p == 'g') g = p;
        else if(*p == 'b') b = p;
        else if(*p == 'a') a = p;
    }
    if( (r == nullptr) || (g == nullptr) || (b == nullptr) )
        throw;
    else if(a == nullptr)
        return RGBA(atoi(r + 1), atoi(g + 1), atoi(b + 1));
    else
        return RGBA(atoi(r + 1), atoi(g + 1), atoi(b + 1), atoi(a + 1));
}

int main()
{
    RGBA a = "r1 g1 b1 a1"_C;
    cout << (int)a.r << endl;
}

 

3 属性

C++11提供了属性指定符系列,可人工指示编译器优化,以达到提高程序性能的目的

3.1 noreturn

指定函数没有返回值,有指示编译器不为函数返回生成代码的功能

如:

[[noreturn]] void foo()
{ }

 

3.2 carries_dependency

指定函数不必消除内存顺序一致性,即函数可乱序执行。

如:

[[carries_dependency]] void foo(int a, int b)
{
    a++;//哪一个语句先执行,都不会改变函数的最终结果
    b++;
}

int main()
{
    int a = 0, b = 0;
    foo(a, b);
    return 0;
}

 

4 宏

4.1 __func__

获取当前函数的函数名称。放在构造函数中便可获取类型名称

例子:

const char* foo()
{
    return __func__;//返回"foo"字符串
}

 

4.2 __VA_ARGS__

获取宏的变长参数。

例子1:

#define func(...) printf(__VA_ARGS__) //将所有参数输出到控制台

int main()
{
    func("yohoho", 10.1, 20, 30);
    return 0;
}

例子2:

#define func(type, arg, ...) foo<type>(arg, ##__VA_ARGS__)
//当__VA_ARGS__前面有逗号时,需要加两个#

template<typename T>
void foo(T arg, int num_list, ...)
{
    std::cout << "the type is: " << typeid(T).name() << std::endl;
}

class MyClass
{ };

int main()
{
    func(MyClass, MyClass(), 1, 2, 3, 4, 5, 6, 7);
    return 0;
}

结语

本人已将大多数新特性总结完成,可能非常常用的特性已经标红,纯属个人总结,如有什么不妥或者错误的地方,欢迎指出,谢谢!

发布了20 篇原创文章 · 获赞 1 · 访问量 2786

猜你喜欢

转载自blog.csdn.net/qq811299838/article/details/89303305