【C++】C++11特性

1 原始字面量

C++11 中添加了定义原始字符串的字面量,定义方式为:R “xxx(原始字符串)xxx” 其中()两边的字符串可以省略。xxx为备注,要一样

string s1 = R"(E:/xhh.jpg)";
cout << s1 << endl;

2 nullptr

在这里插入图片描述
在C++中NULL和0是一样的,建议初试化指针选择nullptr,它可以自动转换为各种类型如void * 或者 int * 等等。
使用 nullptr 初始化空指针可以令我们编写的程序更加健壮。

3 常量表达式修饰符 constexpr

在 C++11 之前只有 const 关键字,从功能上来说这个关键字有双重语义:变量只读,修饰常量。

在 C++11 中添加了一个新的关键字 constexpr,这个关键字是用来修饰常量表达式的。所谓常量表达式,指的就是由多个(≥1)常量(值不会改变)组成并且在编译过程中就得到计算结果的表达式。
C++ 程序从编写完毕到执行分为四个阶段:预处理、 编译、汇编和链接 4 个阶段,得到可执行程序之后就可以运行了。

  • 修饰常量
constexpr int num = 888;
  • 修饰函数

为了提高 C++ 程序的执行效率,我们可以将程序中值不需要发生变化的变量定义为常量,也可以使用 constexpr 修饰函数的返回值,这种函数被称作常量表达式函数,这些函数主要包括以下几种:普通函数/类成员函数、类的构造函数、模板函数。

// ok
constexpr int func2()
{
    using mytype = int;
    constexpr mytype a = 100;
    constexpr mytype b = 10;
    constexpr mytype c = a * b;
    return c - (a + b);
}

4 自动类型推导 auto和decltype

使用 auto 自动推导变量的类型,结合 decltype 来表示函数的返回值。使用新的特性可以让我们写出更加简洁,更加现代的代码。

  • 当变量不是指针或者引用类型时,推导的结果中不会保留 const、volatile 关键字
  • 当变量是指针或者引用类型时,推导的结果中会保留 const、volatile 关键字
auto num = 888;
map<int, string> person;
// 代码简化
for (auto it = person.begin(); it != person.end(); ++it)
{
    // do something
}

decltype 是 “declare type” 的缩写,意思是 “声明类型”。decltype 的推导是在编译期完成的,它只是用于表达式类型的推导,并不会计算表达式的值。

int a = 10;
decltype(a) b = 99;                 // b -> int
decltype(a+3.14) c = 52.13;         // c -> double

返回值类型后置

// 符号 -> 后边跟随的是函数返回值的类型
auto func(参数1, 参数2, ...) -> decltype(参数表达式)
#include <iostream>
using namespace std;

template <typename T, typename U>
// 返回类型后置语法
auto add(T t, U u) -> decltype(t+u) 
{
    return t + u;
}

int main()
{
    int x = 520;
    double y = 13.14;
    // auto z = add<int, double>(x, y);  // 显式指定
    auto z = add(x, y);		// *** 简化之后的写法
    cout << "z: " << z << endl;
    return 0;
}

5 final和override

C++ 中增加了 final 关键字来限制某个类不能被继承,或者某个虚函数不能被重写,和 Jave 的 final 关键字的功能是类似的。如果使用 final 修饰函数,只能修饰虚函数,并且要把final关键字放到类或者函数的后面。

C++中如果使用 final 修饰函数,只能修饰虚函数,这样就能阻止子类重写父类的这个函数了。

class Child final: public Base
{
public:
    void test()
    {
        cout << "Child class...";
    }
};


class GrandChild : public Child
{
public:
    void test() override
    {
        cout << "Child class...";
    }
};

6 模板的右尖括号和默认模板参数

6.1 模板的右尖括号

在泛型编程中,模板实例化有一个非常繁琐的地方,那就是连续的两个右尖括号(>>)会被编译器解析成右移操作符,而不是模板参数表的结束。

C++11 支持

$ g++ test.cpp -std=c++11 -o app
6.2 默认模板参数
template <typename T=int>	// C++98/03不支持这种写法, C++11中支持这种写法
void func(T t)
{
    cout << "current value: " << t << endl;
}

7 using新功能

在 C++ 中 using 用于声明命名空间,使用命名空间也可以防止命名冲突。在程序中声明了命名空间之后,就可以直接使用命名空间中的定义的类了。在 C++11 中赋予了 using 新的功能,让 C++ 变得更年轻,更灵活。

  • 定义类型的别名
// [1] 
// typedef 旧的类型名 新的类型名;
// 使用举例
typedef unsigned int uint_t;

// [2]
// using 新的类型 = 旧的类型;
// 使用举例
using uint_t = int;

  • // 定义函数指针类型
int myFunc(int a, string b){
    cout << a << " & " << b << endl;
    return 0;
}


// 定义函数指针类型
// 参数(int, string)  返回值int  函数名func1
// [1] 采用typedef 可读性差
typedef int(*func1)(int, string);

// [2] 采用using 易读
using func2 = int(*)(int, string);



void test_func_ptr(){
    // 函数指针
    func1 f1 = myFunc;
    func2 f2 = myFunc;

    // 使用
    f1(18, "xhh");   // 函数指针,使用函数
    (*f2)(9, "mcy"); // 解引用,使用函数
}
  • 模板定义别名
template <typename T>
using Mmap = map<int, T>;

Mmap<string> m_is;

8 委托构造函数和继承构造函数

  • 委托构造函数
class Test
{
public:
    Test() {};
    Test(int max)
    {
        this->m_max = max > 0 ? max : 100;
    }

    Test(int max, int min):Test(max) // 写在初始化列表中
    {
        this->m_min = min > 0 && min < max ? min : 1;
    }

    Test(int max, int min, int mid):Test(max, min)
    {
        this->m_middle = mid < max && mid > min ? mid : 50;
    }

    int m_min;
    int m_max;
    int m_middle;
};
  • 继承构造函数

C++11 以前的方式

class Base
{
public:
    Base(int i) :m_i(i) {}
    Base(int i, double j) :m_i(i), m_j(j) {}
    Base(int i, double j, string k) :m_i(i), m_j(j), m_k(k) {}

    int m_i;
    double m_j;
    string m_k;
};

class Child : public Base
{
public:
	// 重复
    Child(int i) :Base(i) {}
    Child(int i, double j) :Base(i, j) {}
    Child(int i, double j, string k) :Base(i, j, k) {}
};

C++11 继承构造的方式

class Base
{
public:
    Base(int i) :m_i(i) {}
    Base(int i, double j) :m_i(i), m_j(j) {}
    Base(int i, double j, string k) :m_i(i), m_j(j), m_k(k) {}

    int m_i;
    double m_j;
    string m_k;
};

class Child : public Base
{
public:
    using Base::Base; // 继承父类的所有构造函数
};

9 初始化列表

在 C++98/03 中,对应普通数组和可以直接进行内存拷贝(memcpy ())的对象是可以使用列表初始化来初始化数据的

// 数组的初始化
int array[] = { 1,3,5,7,9 };
double array1[3] = { 1.2, 1.3, 1.4 };

// 对象的初始化
struct Person
{
    int id;
    double salary;
}zhang3{ 1, 3000 };

在 C++11 中,列表初始化变得更加灵活了,来看一下下面这段初始化类对象的代码:

#include <iostream>
using namespace std;

class Test
{
public:
    Test(int) {}
private:
    Test(const Test &);
};

int main(void)
{
    Test t1(520);
    Test t2 = 520; 
    Test t3 = { 520 }; // C++11新特性
    Test t4{ 520 };    // C++11新特性
    
    int a1 = { 1314 };
    int a2{ 1314 };
    
    int arr1[] = { 1, 2, 3 };
    int arr2[]{ 1, 2, 3 };
    return 0;
}
  • std::initializer_list<T>

在 C++ 的 STL 容器中,可以进行任意长度的数据的初始化,使用初始化列表也只能进行固定参数的初始化,如果想要做到和 STL 一样有任意长度初始化的能力,可以使用 std::initializer_list 这个轻量级的类模板来实现。

内部有三个成员接口:size(), begin(), end()

initializer_list<int> nums = {11, 13, 15};

10 基于范围的for循环

C++11 基于范围的 for 循环,语法格式:

for (declaration : expression)
{
    // 循环体
}
vector<int> t{ 1,2,3,4,5,6 };

// for (auto value : t) //拷贝,浪费资源
for (auto &value : t) // 利用引用,这样还可以改原容器中的值
// for (const auto &value : t) // 利用只读引用,提高读效率
{
    cout << value << " ";
}
  • 关系型 k-v
#include <iostream>
#include <string>
#include <map>
using namespace std;

int main(void)
{
    map<int, string> m{
        {1, "lucy"},{2, "lily"},{3, "tom"}
    };

    // 基于范围的for循环方式  pair对象
    for (auto& it : m)
    {
        cout << "id: " << it.first << ", name: " << it.second << endl;
    }

    // 普通的for循环方式   指针
    for (auto it = m.begin(); it != m.end(); ++it)
    {
        cout << "id: " << it->first << ", name: " << it->second << endl;
    }

    return 0;
}

11 可调用对象包装器、绑定器

11.1可调用对象

在 C++ 中存在 “可调用对象” 这么一个概念。准确来说,可调用对象有如下几种定义:

  • 是一个函数指针
int print(int a, double b)
{
    cout << a << b << endl;
    return 0;
}

// 定义函数指针
int (*func)(int, double) = &print;
  • 是一个具有operator()成员函数的类对象(仿函数)
#include <iostream>
#include <string>
#include <vector>
using namespace std;

struct Test
{
    // ()操作符重载
    void operator()(string msg)
    {
        cout << "msg: " << msg << endl;
    }
};

int main(void)
{
    Test t;
    t("我是要成为海贼王的男人!!!");	// 仿函数
    return 0;
}
  • 是一个可被转换为函数指针的类对象
#include <iostream>
#include <string>
#include <vector>
using namespace std;

using func_ptr = void(*)(int, string);
struct Test
{
    static void print(int a, string b)
    {
        cout << "name: " << b << ", age: " << a << endl;
    }

    // 将类对象转换为函数指针
    operator func_ptr()
    {
        return print;
    }
};

int main(void)
{
    Test t;
    // 对象转换为函数指针, 并调用
    t(19, "Monkey D. Luffy");

    return 0;
}
  • 是一个类成员函数指针或者类成员指针
#include <iostream>
#include <string>
#include <vector>
using namespace std;

struct Test
{
    void print(int a, string b)
    {
        cout << "name: " << b << ", age: " << a << endl;
    }
    int m_num;
};

int main(void)
{
    // 定义类成员函数指针指向类成员函数
    void (Test::*func_ptr)(int, string) = &Test::print;
    // 类成员指针指向类成员变量
    int Test::*obj_ptr = &Test::m_num;

    Test t;
    // 通过类成员函数指针调用类成员函数
    (t.*func_ptr)(19, "Monkey D. Luffy");
    // 通过类成员指针初始化类成员变量
    t.*obj_ptr = 1;
    cout << "number is: " << t.m_num << endl;

    return 0;
}
11.2 包装器

std::function是可调用对象的包装器。它是一个类模板,可以容纳除了类成员(函数)指针之外的所有可调用对象。通过指定它的模板参数,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟执行它们。

#include <functional>
std::function<返回值类型(参数类型列表)> diy_name = 可调用对象;

pass

12 Lambda表达式(匿名函数)

[capture](params) opt -> ret {body;};

在这里插入图片描述

void test_xhh_06(){
    [](){
      cout << "xhh" << endl;
    }();
}

// 完整的lambda表达式定义
auto f = [](int a) -> int
{
    return a+10;  
};

// 忽略返回值的lambda表达式定义
auto f = [](int a)
{
    return a+10;  
};

在这里插入图片描述

13 右值引用

13.1 解释左/右值
  • 左值是指存储在内存中、有明确存储地址(可取地址)的数据
  • 右值是指可以提供数据值的数据(不可取地址)

可以对表达式取地址(&)就是左值,否则为右值。

int value = 520;

value 是左值,520 是字面量也就是右值。其中 value 可以被引用,但是 520 就不行了,因为字面量都是右值。

13.2 右值引用

pass

14 move和forward

14.1 move

使用std::move方法可以将左值转换为右值。
使用这个函数并不能移动任何东西,而是和移动构造函数一样都具有移动语义,将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存拷贝。

// std::move 基本等同于一个类型转换:static_cast<T&&>(lvalue);

class Test
{
publicTest(){}
    ......
}
int main()
{
    Test t;
    decltype(x) && v1 = t;          // error  
    decltype(x) && v2 = move(t);    // ok     右值引用,转移
    return 0;
}



// 资源的转移
list<string> ls;
ls.push_back("hello");
ls.push_back("world");
......
list<string> ls1 = ls;        // 需要拷贝, 效率低,但ls1和ls2都在
list<string> ls2 = move(ls);  // 
14.2 forward

pass

15 智能指针

在 C++ 中没有垃圾回收机制,必须自己释放分配的内存,否则就会造成内存泄露。解决这个问题最有效的方法是使用智能指针(smart pointer)。智能指针是存储指向动态分配(堆)对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动地销毁动态分配的对象,防止内存泄露。智能指针的核心实现技术是引用计数,每使用它一次,内部引用计数加1,每析构一次内部的引用计数减1,减为0时,删除所指向的堆内存。

#include <memory>
  • std::shared_ptr:共享的智能指针
  • std::unique_ptr:独占的智能指针
  • std::weak_ptr:弱引用的智能指针,它不共享指针,不能操作资源,是用来监视 shared_ptr 的。
15.1 共享智能指针
// 管理当前对象的 shared_ptr 实例数量,或若无被管理对象则为 0。
long use_count() const noexcept;
1 初始化共享智能指针
// shared_ptr<T> 类模板中,提供了多种实用的构造函数, 语法格式如下:
std::shared_ptr<T> 智能指针名字(创建堆内存);
shared_ptr<Person> p1(new Person);

// 通过拷贝和移动构造函数初始化
shared_ptr<Person> p2(p1);
shared_ptr<Person> p3 = move(p1);
 
// 通过 std::make_shared 初始化
shared_ptr<Person> pp1 = make_shared<Person>();

2 获取原始指针

对应基础数据类型来说,通过操作智能指针和操作智能指针管理的内存效果是一样的,可以直接完成数据的读写。但是如果共享智能指针管理的是一个对象,那么就需要取出原始内存的地址再操作,可以调用共享智能指针类提供的 get () 方法得到原始地址,其函数原型如下:

T* get() const noexcept;
shared_ptr<Person> pp1 = make_shared<Person>();

// [1] 直接操作
pp1->name = "mcy";
cout << pp1->name << endl;

// [2] 获取原类型 操作
Person *from_pp1 = pp1.get();
from_pp1->name = "xhh";
cout << from_pp1->name << endl;
3 指定删除器

当智能指针管理的内存对应的引用计数变为 0 的时候,这块内存就会被智能指针析构掉了。另外,我们在初始化智能指针的时候也可以自己指定删除动作,这个删除操作对应的函数被称之为删除器,这个删除器函数本质是一个回调函数,我们只需要进行实现,其调用是由智能指针完成的。

int main()
{
    shared_ptr<int> ptr(new int[10], [](int* p) {delete[] p; });
    return 0;
}
15.2 独占的智能指针

std::unique_ptr 是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,可以通过它的构造函数初始化一个独占智能指针对象,但是不允许通过赋值将一个 unique_ptr 赋值给另一个 unique_ptr。

1 初始化
// 创建
unique_ptr<Person> p1(new Person);

// 移动
unique_ptr<Person> p2 = move(p1);

// reset 释放原来的控制,获取新的指向
p2.reset(new Person);

// 获取
Person *from_up = p2.get();
2 删除器
int main()
{
    using func_ptr = void(*)(int*);
    unique_ptr<int, function<void(int*)>> ptr1(new int(10), [&](int*p) {delete p; });
    return 0;
}
15.3 弱引用智能指针

pass

16 数值类型和字符串之间的转换

16.1 数值转换为字符串分
// 头文件 <string>
string to_string(int val);
string to_string(long val);
string to_string(long long val);
string to_string(unsigned val);
string to_string(unsigned long val);
string to_string(unsigned long long val);

参考内容

[1] Bilibli教学视频:c++11实用特性[c/c++项目开发必备技能].

[2] C++11详解剖析.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值