c++11特性

c++11

‘Effective Modern c++’ 英文版,c++新特性讲得不错,需要的戳文末。

初始化列表

数组之外,STL容器和自定义数据类型也支持初始化列表,而且格式统一

initializer_list是轻量级的容器,只有begin(),end(),size(),只能被整体初始化,要求内部类型一致。

初始化列表可以阻止类型收窄,即从高精度到低精度的隐式转化

#include <initializer_list>
std::map<int, std::string> m = { {1, "a"}, {2, "b"} }; //STL容器

class A {
public:
    B(const std::initializer_list<int>& items): m_items(items){}
private:
    std::vector<int> m_items;
};

A a1{ 1, 2, 3 };  //自定义数据类型

类型推导

auto: 主要用于容器迭代器,也可以推导函数返回类型

  • 当不声明为指针或引用时,推导结果会抛弃引用和volatile/const
  • 声明为指针或引用类型时,会保留volatile/const
  • 隐式类型定义的类型推导发生在编译器,而不像python发生在运行期

decltype: 推导表达式的类型

template<typename T, typename U>
auto add(T x, U y) -> decltype(x+y) {  //拖尾返回类型
    return x+y;
}

template<typename T, typename U>  //c++14中一般函数允许返回值推导
auto add(T x, U y) {
    return x+y;
}

空指针

NULL实际上是一个为0的int值

nullptr类型为nullptr_t

//f(NULL)调用,引发歧义,f(nullptr)调用
void f(int i) {}
void f(int* arr) {}

for-each循环

1)对于 map 这种关联性容器而言,for 循环中 val 的类型是 std::pair需要使用 val.firstval.second 来提取键值。

2)auto 自动推导出的类型是容器中的 value_type,而不是迭代器。

3)基于范围的 for 循环倾向于在循环开始之前确定好迭代的范围,在循环中修改容器也许会导致异常

4)适合容器,区间迭代还是使用普通for数组

for (std::vector<int>::const_iterator itr = v.begin(); itr != v.end(); ++itr) {
    std::cout << *itr << std::endl;  //迭代器声明和解引用
}

for (int i : v) {  //每次循环会拷贝一次i,如果开销大可考虑引用
    std::cout << i << std::endl;
}

强枚举类型

enum class Direction {  //关键字从enum变成enum class
    Left, Right
};
enum class Answer {
    Right, Wrong
};
auto a = Direction::Left;  //引用时必须加上枚举名称,枚举值不再是全局的
auto b = Answer::Right;

if (a == b)
    std::cout << "a == b" << std::endl;
else
    std::cout << "a != b" << std::endl;

using关键字

取代typedef定义别名
using cbyte = char
定义模板别名,typedef实现比较麻烦
 template<class T>
 using Tlist = std::list<T>;
 using Tlist = std::list<char>;
在子类引用父类成员
class Base
{
    public:
        void func() { // some code}
        int func(int n) { // some code}
}
 
class Sub : public Base
{
    public:
        using Base::func;
        void func() { // some code}      
}
 
int main()
{
    Sub s;
    s.func();
    s.func(1); // Success!
}

final禁止

禁止虚函数被重写
class A {
public:
    virtual void f1() final {}
};

class B : public A {
    virtual void f1() {}  //报错
};
禁止类被继承
class A final {
};

class B : public A {  //报错
};

override显式声明

class A {
public:
    virtual void f1() const {}
};

class B : public A {
    virtual void f1() override {} //编译器会报错,不加override会认为子类新增函数f1
};

default默认构造函数

当自定义构造函数时,编译器不会自动生成无参构造函数,可以使用default关键字强制要求生成。

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

delete禁止函数调用

隐藏函数不必声明为private,可以使用delete关键字

Lambda匿名函数

auto add = [](int a, int b) { return a + b; };
捕获列表 作用
[] 不捕获变量
[a] a为值传递
[a, &b] a为值传递,b为引用传递
[&] 所有变量都用引用传递。当前对象(即this指针)也用引用传递。
[=] 所有变量都用值传递。当前对象用引用传递。

注意:lambda访问上下文的局部变量时不能超过其生命周期

随机数生成器

控制范围的均匀分布,不需要初始化种子

#include <random>
#include <iostream>
using namespace std;
int main()
{
    mt19937 mt(random_device{}());
    cout<<mt()%100;
}

字符串数值类型转换

字符串<<--数值
std::string to_string(int value);  //支持long,double,unsigned等其他数据类型
字符串-->>数值

stoi、stol、stoul、stoll、stoull、stof、stod、stold函数

委托构造函数

避免有多个参数表不同但是逻辑相近(或者有公共部分)的构造函数的时候的代码重复。

class A
{
    int i;
    double d;
public:
    A(int x1,double x2) :i(x1),d(x2){
        //do something here
    }
    A():A(0,0){}
    A(int i):A(i,0) {}
};

移动语义

std::move将一个左值强制转化为右值引用,对象的状态或者所有权从一个对象转移到另一个对象,没有内存搬迁或者内存拷贝

  • 左值是指表达式结束后依然存在的持久化对象
  • 右值是指表达式结束时就不再存在的临时对象
    std::string str = "Hello";
    std::vector<std::string> v;
    //调用常规的拷贝构造函数,新建字符数组,拷贝数据
    v.push_back(str);
    //调用移动构造函数,无内存的额外创建,str在move之后为空
    v.push_back(std::move(str));

智能指针

shared_ptr

多个指针指向相同对象,当最后一个shared_ptr离开作用域时,内存才自动释放。

shared_ptr<int> sptr1( new int );  //先申请数据内存,再申请内存块
shared_ptr<int> sptr1 = make_shared<int>(“Hello”);  //一次申请,但weak_ptr会锁内存

接口:

  1. get(): 获取shared_ptr绑定的资源
  2. unique():判断是否唯一指向当前内存的shared_ptr
  3. reset():释放关联内存块的所有权,如果是最后一个指向该资源的指针,释放该内存
  4. operation bool :判断当前的shared_ptr是否指向一个内存块
  5. swap:交换指针
  6. use_count:返回引用计数

注意:

  • 避免从naked pointer创建shared_ptr(不同的shared_ptr指向一个资源)

  • 循环引用问题

  • 多线程访问同一个shared_ptr需要加锁,shared_ptr引用计数是安全的,对象读写操作不是。

weak_ptr

指向shared_ptr对象,赋值不会增加引用计数

接口:

  1. expire():如果use_count()为零,返回true,否则返回false
  2. lock():如果expire为true,返回空的shared_ptr,否则返回一个指向该弱指针的shared_ptr
  3. use_count():共享的shared_ptr数量
unique_ptr
unique_ptr<int[ ]> uptr( new int[5] ); //创建指向数组类型的unique_ptr

注意:

  • 不支持直接拷贝,但支持移动语义
  • release()只释放所有权,reset()释放所有权和资源

noexcept

c++的异常处理是在运行时而不是编译时检测,编译器会生成额外的代码。

noexcept告诉编译器函数不会发生异常,但如果发生异常,直接终止程序。

void swap(Type& x, Type& y) noexcept{  //整个函数都不会发生异常
    x.swap(y);  
}
void swap(Type& x, Type& y) noexcept(noexcept(x.swap(y))){  //x.swap(y)不发生异常,那么函数就不会异常
    x.swap(y);
}

鼓励使用noexcept的情形:

  • 移动构造函数
  • 移动分配函数
  • 析构函数

引用Effective Modern c++的一句话描述"noexcept is particularly valuable for the move operations, swap, memory deallocation functions, and destructors."

模板默认参数

c++11支持函数模板默认参数,类模板默认参数指定默认值必须从右往左;

若能从实参推导类型,则放弃默认模板参数

书籍链接在这里Effective Modern c++,提取码是我的id

posted @ 2020-08-06 15:55  kite97  阅读(168)  评论(0编辑  收藏  举报