cpp 11新特性(通用性能的提升)

cpp 11新特性(通用性能的提升)

1,constexpr

1.1 概念

由于 const 具有双重语义所以在 cpp11 中引入了constexpr 用来修饰常量表达式

建议使用 constexpr 修饰常量,使用 const 修饰变量只读

1.2 注意

  • 当修饰自定义类型的时候,不能直接修饰,而是在定义实例的时候进行修饰
  • constexptr 也可用来修饰函数但需要注意以下几点
    • 函数必须要有返回值
    • 函数内不能出现常量表达式以外的语句(using typedef static_assert return 除外)
    • 修饰构造函数时函数体必须为空,使用参数列表初始化

1.3 代码实现

#include<iostream>
using namespace std;
class test{
public:
    constexpr test():age(2),sex(1){

    }
    int age;
    int sex;
};
constexpr int func(){
    return 2+5;
}
int main(){
    constexpr test t();
    constexpr int num = 1;
    cout<<num<<endl;
    cout<<func()<<endl;
    return 0;
}

2,委托构造函数和继承构造函数

2.1 概念

委托构造函数:在一个类中,一个构造函数可以调用这个类中的其他的构造函数

继承构造函数:派生类可以使用父类的构造函数

2.2 注意

  • 必须在参数列表中调用其他的构造函数
  • 在委托构造函数初始化某个成员后,不可在构造函数重复初始化
  • 防止环状委托构造
  • 如果子类隐藏了父类的同名函数可以使用 using 来显示该函数

2.3 代码实现

#include<iostream>
using namespace std;
class test{
public:
    test(int age){
        mAge = age;
    }
    test(int age,int sex):test(age){
        mSex = sex;
    }
    int mAge;
    int mSex;
};
class father{
public:
    father(int age,int sex){
        mAge = age;
        mSex = sex;
    }
    int mAge;
    int mSex;
};
class son:public father{
public:
    using father::father;
};

int main(){
    test t1(1,2);
    cout<<t1.mAge<<" "<<t1.mSex<<endl;

    son s1(1,2);
    cout<<s1.mAge<<" "<<s1.mSex<<endl;
    return 0;
}

3,右值引用

3.1 概念

右值引用通常用于移动拷贝和移动构造函数

纯右值:字面量

将亡值:临时变量

3.2 注意

  • 当使用右值引用时,该引用变成为了可取地址的左值
  • &&不一定是右值引用的意思,当模板函数使用时则表示未定的引用类型

3.3 代码实现

#include<iostream>
#include<stdio.h>
using namespace std;

int main(){
    int &&num = 5;
    printf("%p",&num);
    return 0;
}

4,移动语义和完美转发

4.1 概念

移动语义:为了节省资源,一个类可以使用移动构造函数和拷贝构造函数来移动得到别的对象的资源,move 函数将左值转换成右值来达到这个目的
完美转发:向其他函数转发参数的时候区分左右值

4.2 注意

4.3 代码实现

#include<iostream>
using namespace std;
class test{
public:
    test(){
        mAge = new int(5);
    }
    test(test&& t){
        mAge = t.mAge;
        t.mAge = nullptr;
    }
    int *mAge;
};
void func(int& t){
    cout<<"this is a left value"<<endl;
}
void func(int&& t){
    cout<<"this is a right value"<<endl;
}
template<class T>
void test1(T&& t){
    func(forward<T>(t));
};
int main(){
    test t1;
    test t2(move(t1));

    int x = 5;
    test1(5);
    test1(x);
    return 0;
}

5,列表初始化

5.1 概念

cpp 11 中引入了列表初始化,来为数组,对象等进行统一的初始化

5.2 注意

  • 属于聚合体的类才能被直接列表初始化,聚合体类应符合以下几点

    • 使用默认构造函数
    • 无保护和私有的非静态数据成员
    • 无基类
    • 无虚函数
  • 非聚合体类初始化时,可以使用构造函数进行

5.3 代码实现

#include<iostream>
using namespace std;
class test1{
public:
    int age;
    int sex;
};
class test2{
public:
    test2(int age,int sex):age(age),sex(sex){

    }
private:
    int age;
    int sex;
};
int main(){
    test1 t1{1,2};
    test2 t2{1,2};
    return 0;
}

6,using 的使用

6.1 概念

using可以用来声明命名空间也可以用来定义别名

6.2 注意

  • using相对于typedef定义别名更为直观且可以直接定义模板别名

6.3 代码实现

#include<iostream>
using namespace std;
template<class T>
using myMap = map<int,T>;

int main(){
    using myInt = int;
    myInt num = 5;
    cout<<num<<endl;

    myMap<int> map1;
    map1.insert(make_pair(1,1));
    map1.insert(make_pair(2,2));

    for(auto i:map1){
        cout<<i.first<<" "<<i.second<<endl;
    } 
    int i;
    cin>>i;
    return 0;
}

7,可调用对象包装器、绑定器

7.1 概念

可调用对象包装器用来包装可调用对象,绑定器用来绑定全部或者一部分函数参数

7.2 注意

  • 可调用对象包括

    • 函数指针
    • 仿函数
    • 是一个可被转换为函数指针的类对象
    • 类成员函数指针或者类变量指针
  • 函数包装器可以用来当回调函数使用

7.3 代码实现

#include<iostream>
#include<functional>
using namespace std;
void test(int a,int b,int c){
    cout<<a<<" "<<b<<" "<<c<<endl;
}
void func(function<void(int,int,int)> f){
    f(1,2,3);
}
int main(){
    function<void(int,int,int)>f = test;
    f(1,2,3);
    auto func1 = bind(test,1,2,3);
    func1();
    auto func2 = bind(test,placeholders::_1,2,3);
    func2(1);
    func(test);
    sleep(100);
    return 0;
}

8,POD 类型

8.1 概念

pod 类型包含”平凡类型”和”标准布局类型”

  • 平凡类型:
    • 使用默认构造函数和析构函数
    • 使用默认拷贝构造函数和赋值构造函数
    • 使用默认拷贝赋值函数和移动赋值函数
    • 不包含虚函数和虚基类
  • 标准布局类型:
    • 所有非静态成员具有相同的访问权限
    • 类和结构体继承时:
      • 派生类中有非静态成员,基类中包含静态成员
      • 基类有非静态成员,而派生类没有非静态成员。
    • 子类中第一个非静态成员和基类不同
    • 没有虚函数和虚基类
    • 所有非静态数据成员均符合标准布局类型,其基类也符合标准布局,这是一个递归的定义。

8.2 注意

  • 默认构造函数是指无参的默认构造函数,自己实现的有参构造函数不在此列

8.3 代码

#include<iostream>
using namespace std;
class test{
public:
    test() = default;
    test(int a){

    }
};
int main(){
    cout<<is_pod<test>::value<<endl;  
    sleep(100);
    return 0;
}

9,默认函数控制 =default 与 =delete

9.1 概念

=defalut 声明使用默认版本的函数

=delete 声明不使用某些函数

9.2 注意

9.3 代码实现

#include<iostream>
using namespace std;
class test{
public:
    test() = default;
    test(int a){

    }
    void print() = delete;
};
int main(){
    test t1();
    test t2(1);
    t2.prtint()//error:类成员没有 print 函数;  
    sleep(100);
    return 0;
}

10,扩展的 friend 语法

10.1 概念

在cpp11中可以直接用 friend + 类名 来声明友元了

可以为模板声明友元

10.2 注意

10.3 代码实现

#include<iostream>
using namespace std;
class test2{
public:
    void invoke(auto& t){
        cout<<"invoke the tese1 private mumber:"<<t.a<<endl;
    }    
};
class test1{
public:
    friend test2;
    void invoke(auto& t){
        cout<<"invoke the tese1 private mumber:"<<t.a<<endl;
    }   
private:
    int a;
};
template<class T>
class test3{
public:
    friend T;
private:
    int a = 1;
};
int main(){
    test1 t1;
    test2 t2;
    t2.invoke(t1);


    test3<test1>t3; 
    test3<test2>t4;
    t1.invoke(t3);
    t2.invoke(t4); 
    sleep(100);
    return 0;
}

11,强类型枚举

11.1 概念

在cpp11 中引入了强类型枚举,使用 enum class 定义

11.2 注意

  • 强类型枚举相对于普通类型的枚举有了作用域的限制
  • 强类型枚举不可以和整形隐式转换
  • 可以指定值的类型

11.3 代码实现

#include<iostream>
using namespace std;
enum class China { Shanghai, Dongjing, Beijing, Nanjing, };
enum class Japan:char { Dongjing, Daban, Hengbin, Fudao };
int main()
{
    int m = Shanghai;           // error
    int n = China::Shanghai;    // error
    if ((int)China::Beijing >= 2)
    {
    	cout << "ok!" << endl;
    }
    cout << "size1: " << sizeof(China::Dongjing) << endl;
    cout << "size2: " << sizeof(Japan::Dongjing) << endl;
    return 0;
}

12,非受限联合体

12.1 概念

在 cpp11 中取消了联合体数据成员的限制条件,规定了任何非引用的数据类型都可以作为联合体数据成员

12.2 注意

12.3 代码实现

#include<iostream>
using namespace std;
union unio1
{
    int age;
    long id;
    static char c;
    static int print()
    {
        cout << "c value: " << c << endl;
        return 0;
    }
};
char unio1::c = 'a';
int main(){
    unio1 test;
    test.age = 1;
    test.id = 2;
    cout<<test.age<<test.id<<test.c;
    unio1::print();
    sleep(500);
}