CPP-14-11新特性

类型推导

C++11引入了autodecltype关键字,使用他们可以在编译期就推导出变量或者表达式的类型,方便开发者编码也简化了代码

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

    1
    auto a = 10; // 10是int型,可以自动推导出a是int
  • decltype:相对于auto用于推导变量类型,而decltype用于推导表达式类型,这里只用于编译器分析表达式的类型,表达式实际不会进行运算

    1
    2
    3
    cont int &i = 1;
    int a = 2;
    decltype(i) b = 2; // b是const int&

右值引用

右值引用是 C++11 引入的一个重要特性,它允许程序员有效地处理临时(即将销毁的)对象,并支持移动语义,从而提高性能和资源利用率。右值引用通常与移动语义一起使用,用于优化对象的传递、复制和销毁操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <vector>
using namespace std;

class MyString {
public:
MyString(const char* str) : data(new char[strlen(str) + 1]) {
cout << "point 2" << endl;
strcpy(data, str);
}

// 移动构造函数
MyString(MyString&& other) : data(other.data) {
cout << "point 1" << endl;
other.data = nullptr;
}

~MyString() {
delete[] data;
}

void Show() {
cout << data << endl;
}

private:
char* data;
};

int main() {
MyString str1("Hello, world!");
str1.Show();

// 使用 std::move 调用移动构造函数
MyString str2 = std::move(str1);
// str1.Show(); //segmentation fault
str2.Show();

return 0;
}

输出结果

1
2
3
4
point 2
Hello, world!
point 1
Hello, world!

移动构造函数是一种特殊的构造函数,用于实现从一个对象(通常是临时对象或右值)移动数据到另一个对象,而不是进行深拷贝。移动构造函数的存在可以显著提高性能,特别是对于大型资源,如动态分配的内存

移动构造函数通常采用右值引用作为参数,即 Type&&,其中 Type 是类的类型。在移动构造函数内部,我们可以执行数据指针的转移、资源的所有权转移等操作,以实现高效的移动语义

列表初始化

在C++11中可以直接在变量名后面加上初始化列表来进行对象的初始化

1
2
3
4
5
6
7
Time::Time(int h, int m)
{
hours = h;
minutes = m;
}

Time coding(2, 40)

封装

C++11 新增了 std::function & std::bind & Lambda表达式

std::function

std::function 是一种通用、多态的函数封装。可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、Lambda表达式、函数指针、以及其它函数对象等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//代码出自链接:http://www.jellythink.com/archives/771
#include <functional>
#include <iostream>
using namespace std;

std::function<int(int)> Functional;

// 普通函数
int TestFunc(int a)
{
return a;
}

// Lambda表达式
auto lambda = [](int a)->int{ return a; };

// 仿函数(functor)
class Functor
{
public:
int operator()(int a)
{
return a;
}
};

// 1.类成员函数
// 2.类静态函数
class TestClass
{
public:
int ClassMember(int a) { return a; }
static int StaticMember(int a) { return a; }
};

int main()
{
// 普通函数
Functional = TestFunc;
int result = Functional(10);
cout << "普通函数:"<< result << endl;

// Lambda表达式
Functional = lambda;
result = Functional(20);
cout << "Lambda表达式:"<< result << endl;

// 仿函数
Functor testFunctor;
Functional = testFunctor;
result = Functional(30);
cout << "仿函数:"<< result << endl;

// 类成员函数
TestClass testObj;
Functional = std::bind(&TestClass::ClassMember, testObj, std::placeholders::_1);
result = Functional(40);
cout << "类成员函数:"<< result << endl;

// 类静态函数
Functional = TestClass::StaticMember;
result = Functional(50);
cout << "类静态函数:"<< result << endl;

return 0;
}

运行结果

1
2
3
4
5
普通函数:10
Lambda表达式:20
仿函数:30
类成员函数:40
类静态函数:50
  • 转换后的std::function对象的参数能转换为可调用实体的参数;可调用实体的返回值能转换为std::function对象的返回值。
  • std::function对象最大的用处就是在实现函数回调,但注意,它不能被用来检查相等或者不相等,但是可以与NULL或者nullptr进行比较

std::bind

std::bind 是 C++11 标准引入的函数模板,位于 <functional> 头文件中。它用于创建一个新的可调用对象(函数对象或函数指针),将一个函数与其参数绑定起来,可以实现参数的预绑定、参数重排序以及参数传递等功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <functional>
using namespace std;

// 原始函数
void greet(const string &name, const string &message, const string &result) {
cout << "Hello, " << name << "! " << message << ", "<< result <<endl;
}

int main() {
// 使用 bind 创建一个新的函数对象,并绑定第一个参数为 "Alice"
auto greetAlice = bind(greet, "Alice", placeholders::_1, "Bye");
auto greetBob = bind(greet, "Bob", placeholders::_1, placeholders::_2);

// 调用新的函数对象
greetAlice("How are you?"); // Output: Hello, Alice! How are you?
greetBob("Nice to meet you", "heihei");
return 0;
}

运行结果

1
2
Hello, Alice! How are you?, Bye
Hello, Bob! Nice to meet you, heihei

其中 placeholders::_1 表示参数被保留为占位符

Lambda 表达式

Lambda 表达式是 C++11 标准引入的一种匿名函数形式,它允许你在代码中内联定义一个简单的函数,无需显式命名。Lambda 表达式通常用于需要一个函数对象(函数符)的地方,例如作为函数参数、STL 算法的谓词、或者用于创建自定义的排序规则

Lambda 表达式的基本语法如下

1
2
3
[capture](parameters) -> return_type {
// 函数体
}
  • capture 是用于捕获外部变量的列表。它可以为空,表示不捕获任何变量,也可以是 [&](捕获所有变量引用)、[=](捕获所有变量拷贝)、[var1, var2](捕获特定变量)等形式。
  • parameters 是传递给 Lambda 表达式的参数列表。
  • return_type 是 Lambda 表达式的返回类型。可以省略,编译器会自动推断。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};

// 使用 Lambda 表达式作为 STL 算法的谓词进行排序
sort(numbers.begin(), numbers.end(), [](int a, int b) {
return a < b;
});

// 使用 Lambda 表达式打印排序后的结果
cout << "Sorted numbers:";
for (int num : numbers) {
cout << " " << num;
}
cout << endl;

return 0;
}

另外 捕获引用[&]与捕获复制[=]的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
using namespace std;

int main() {
int x = 10, y = 20;
int m = 100, n = 200;

// 使用 Lambda 表达式引用捕获外部变量 x 和 y
auto lambda1 = [&]() {
cout << "x: " << x << ", y: " << y << endl;
x += 5;
y += 10;
};

lambda1(); // 调用 Lambda 表达式

auto lambda2 = [=]() mutable {
cout << "x: " << m << ", y: " << n << endl;
m += 5;
n += 10;
};

lambda2();


// 在调用 Lambda 表达式后,x 和 y 的值被修改
cout << "After lambda call - x: " << x << ", y: " << y << endl;
cout << "After lambda call - m: " << m << ", n: " << n << endl;

return 0;
}

这里不同的是,尝试对以值方式(copy captured)捕获的变量进行赋值操作。默认情况下,Lambda 表达式的参数和以值方式捕获的变量都是不可变的,即不能被修改

  • 希望在 Lambda 表达式内部修改这些变量,需要将 Lambda 表达式标记为可变mutable
  • 另外已值方式捕获的变量不会影响 main 函数的 mn, 因为值捕获创建了变量的拷贝

模板改进

C++11关于模板有一些细节的改进:

  • 模板的右尖括号
  • 模板的别名
  • 函数模板的默认模板参数

模板的右尖括号(Template Right Angle Brackets): 在 C11 之前,当使用嵌套的模板类型时,右尖括号 >> 可能会被解释为右移操作符。C11 引入了对 >> 在模板中的正确解析,以避免这种歧义。这样可以更容易地编写嵌套的模板类型,例如容器嵌套容器。

1
std::vector<std::vector<int>> matrix; // 在 C++11 及以后版本中正确解析

模板的别名(Template Aliases): C++11 引入了模板的别名,可以使用 using 关键字来定义模板别名,从而为模板类型创建简洁易读的名称。这对于复杂的模板类型特别有用

1
2
3
4
template <typename T>
using Vector = std::vector<T>; // 定义模板别名 Vector

Vector<int> numbers; // 等同于 std::vector<int>

函数模板的默认模板参数(Default Template Arguments for Function Templates): C++11 允许在函数模板中为模板参数指定默认值,从而使调用函数模板时可以省略特定的模板参数。这可以简化模板函数的调用,并提高代码的可读性

1
2
3
4
5
6
7
8
9
10
template <typename T = int>
void printValue(T value) {
std::cout << value << std::endl;
}

// 调用函数模板时可以省略模板参数,使用默认类型 int
printValue(42);

// 也可以显式指定模板参数类型
printValue<double>(3.14);

并发

有点多,后面单独弄一章单线程

智能指针

智能指针(Smart Pointers)用于管理动态分配的内存资源,有助于避免内存泄漏、多重释放等常见的内存错误

C++11引入了三种主要类型的智能指针:

  1. std::shared_ptr: 共享指针,用于多个智能指针共享同一个资源。资源在最后一个std::shared_ptr离开其作用域时释放

    1
    2
    3
    4
    #include <memory>

    std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
    std::shared_ptr<int> anotherSharedPtr = sharedPtr; // 共享资源
  2. std::unique_ptr: 独占指针,用于唯一拥有一个资源,保证了资源的独占性和自动释放

    1
    2
    3
    4
    #include <memory>

    std::unique_ptr<int> uniquePtr = std::make_unique<int>(42);
    // std::unique_ptr<int> anotherUniquePtr = uniquePtr; // 错误,无法复制,但可以通过std::move移动
  3. std::weak_ptr: 弱指针,用于共享资源的控制,但不会增加资源的引用计数。通常与std::shared_ptr一起使用,以防止循环引用

    1
    2
    3
    4
    5
    #include <memory>

    std::shared_ptr<int> shared = std::make_shared<int>(42);
    std::weak_ptr<int> weak = shared; // 弱指针
    std::shared_ptr<int> sharedAgain = weak.lock(); // 尝试获取shared_ptr

基于范围的for循环

1
2
3
4
5
6
7
8
9
vector<int> vec;

for (auto iter = vec.begin(); iter != vec.end(); iter++) { // before c++11
cout << *iter << endl;
}

for (int i : vec) { // c++11基于范围的for循环
cout << "i" << endl;
}

委托构造函数

委托构造函数允许在同一个类中一个构造函数调用另外一个构造函数,可以在变量初始化时简化操作,通过代码来感受下委托构造函数的妙处:

不使用委托构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct A {
A(){}
A(int a) { a_ = a; }

A(int a, int b) { // 好麻烦
a_ = a;
b_ = b;
}

A(int a, int b, int c) { // 好麻烦
a_ = a;
b_ = b;
c_ = c;
}

int a_;
int b_;
int c_;
};

使用委托构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
struct A {
A(){}
A(int a) { a_ = a; }

A(int a, int b) : A(a) { b_ = b; }

A(int a, int b, int c) : A(a, b) { c_ = c; }

int a_;
int b_;
int c_;
};

继承构造函数

继承构造函数可以让派生类直接使用基类的构造函数,如果有一个派生类,希望派生类采用和基类一样的构造方式,可以直接使用基类的构造函数,而不是再重新写一遍构造函数,老规矩,看代码:

不使用继承构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Base {
Base() {}
Base(int a) { a_ = a; }

Base(int a, int b) : Base(a) { b_ = b; }

Base(int a, int b, int c) : Base(a, b) { c_ = c; }

int a_;
int b_;
int c_;
};

struct Derived : Base {
Derived() {}
Derived(int a) : Base(a) {} // 好麻烦
Derived(int a, int b) : Base(a, b) {} // 好麻烦
Derived(int a, int b, int c) : Base(a, b, c) {} // 好麻烦
};
int main() {
Derived a(1, 2, 3);
return 0;
}

使用继承构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct Base {
Base() {}
Base(int a) { a_ = a; }

Base(int a, int b) : Base(a) { b_ = b; }

Base(int a, int b, int c) : Base(a, b) { c_ = c; }

int a_;
int b_;
int c_;
};

struct Derived : Base {
using Base::Base;
};

int main() {
Derived a(1, 2, 3);
return 0;
}

只需要使用using Base::Base继承构造函数,就免去了很多重写代码的麻烦

nullptr

nullptr是c11用来表示空指针新引入的常量值,在c中如果表示空指针语义时建议使用nullptr而不要使用NULL,因为NULL本质上是个int型的0,其实不是个指针

1
2
3
4
5
6
7
8
9
10
11
12
13
void func(void *ptr) {
cout << "func ptr" << endl;
}

void func(int i) {
cout << "func i" << endl;
}

int main() {
func(NULL); // 编译失败,会产生二义性
func(nullptr); // 输出func ptr
return 0;
}

final & override

c++11关于继承新增了两个关键字,

  • final用于修饰一个类,表示禁止该类进一步派生和虚函数的进一步重载,
  • override用于修饰派生类中的成员函数,标明该函数重写了基类函数,如果一个函数声明了override但父类却没有这个虚函数,编译报错

示例代码1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct Base {
virtual void func() {
cout << "base" << endl;
}
};

struct Derived : public Base{
void func() override { // 确保func被重写
cout << "derived" << endl;
}

void fu() override { // error,基类没有fu(),不可以被重写
}
};

示例代码2:

1
2
3
4
5
6
7
8
9
10
11
struct Base final {
virtual void func() {
cout << "base" << endl;
}
};

struct Derived : public Base{ // 编译失败,final修饰的类不可以被继承
void func() override {
cout << "derived" << endl;
}
};

default

c++11引入default特性,多数时候用于声明构造函数为默认构造函数,如果类中有了自定义的构造函数,编译器就不会隐式生成默认构造函数

1
2
3
4
5
6
7
8
9
struct A {
int a;
A(int i) { a = i; }
};

int main() {
A a; // 编译出错
return 0;
}

上面代码编译出错,因为没有匹配的构造函数,因为编译器没有生成默认构造函数,而通过default,程序员只需在函数声明后加上“=default;”,就可将该函数声明为 defaulted 函数,编译器将为显式声明的 defaulted 函数自动生成函数体

1
2
3
4
5
6
7
8
9
10
struct A {
A() = default;
int a;
A(int i) { a = i; }
};

int main() {
A a;
return 0;
}

delete

c++中,如果开发人员没有定义特殊成员函数,那么编译器在需要特殊成员函数时候会隐式自动生成一个默认的特殊成员函数,例如拷贝构造函数或者拷贝赋值操作符,如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
struct A {
A() = default;
int a;
A(int i) { a = i; }
};

int main() {
A a1;
A a2 = a1; // 正确,调用编译器隐式生成的默认拷贝构造函数
A a3;
a3 = a1; // 正确,调用编译器隐式生成的默认拷贝赋值操作符
}

有时候想禁止对象的拷贝与赋值,可以使用delete修饰,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct A {
A() = default;
A(const A&) = delete;
A& operator=(const A&) = delete;
int a;
A(int i) { a = i; }
};

int main() {
A a1;
A a2 = a1; // 错误,拷贝构造函数被禁用
A a3;
a3 = a1; // 错误,拷贝赋值操作符被禁用
}

delele函数在c++11中很常用,std::unique_ptr就是通过delete修饰来禁止对象的拷贝的

explicit

explicit专用于修饰构造函数,表示只能显式构造,不可以被隐式转换,根据代码看explicit的作用:

不用explicit:

1
2
3
4
5
6
7
8
9
10
struct A {
A(int value) { // 没有explicit关键字
cout << "value" << endl;
}
};

int main() {
A a = 1; // 可以隐式转换
return 0;
}

使用 explicit

1
2
3
4
5
6
7
8
9
10
11
struct A {
explicit A(int value) {
cout << "value" << endl;
}
};

int main() {
A a = 1; // error,不可以隐式转换
A aa(2); // ok
return 0;
}

constexpr

首先说一下 const

  1. const的修饰的变量不可更改

    1
    const int value = 5;
  2. 指针使用const,从右向左读,即可知道const究竟修饰的是指针还是指针所指向的内容

    1
    2
    char *const ptr; // 指针本身是常量
    const char* ptr; // 指针指向的变量为常量
  3. 在函数参数中使用const,一般会传递类对象时会传递一个const的引用或者指针,这样可以避免对象的拷贝,也可以防止对象被修改

    1
    2
    class A{};
    void func(const A& a);
  4. const修饰类的成员变量,表示是成员常量,不能被修改,可以在初始化列表中被赋值

    1
    2
    3
    4
    5
    6
    7
    class A {
    const int value = 5;
    };
    class B {
    const int value;
    B(int v) : value(v){}
    };
  5. 修饰类成员函数,表示在该函数内不可以修改该类的成员变量

    1
    2
    3
    class A{
    void func() const;
    };
  6. 修饰类对象,类对象只能调用该对象的const成员函数

    1
    2
    3
    4
    5
    class A {
    void func() const;
    };
    const A a;
    a.func();

const只表示read only的语义,只保证了运行时不可以被修改,但它修饰的仍然有可能是个动态变量

constexpr修饰的才是真正的常量,它会在编译期间就会被计算出来,整个运行过程中都不可以被改变,constexpr可以用于修饰函数,这个函数的返回值会尽可能在编译期间被计算出来当作一个常量,但是如果编译期间此函数不能被计算出来,那它就会当作一个普通函数被处理

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
using namespace std;

constexpr int func(int i) {
return i + 1;
}

int main() {
int i = 2;
func(i);// 普通函数
func(2);// 编译期间就会被计算出来
}

enum class

不带作用域的枚举代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum AColor {
kRed,
kGreen,
kBlue
};

enum BColor {
kWhite,
kBlack,
kYellow
};

int main() {
if (kRed == kWhite) {
cout << "red == white" << endl;
}
return 0;
}

不带作用域的枚举类型可以自动转换成整形,且不同的枚举可以相互比较,代码中的红色居然可以和白色比较,这都是潜在的难以调试的bug,而这种完全可以通过有作用域的枚举来规避

有作用域的枚举代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum class AColor {
kRed,
kGreen,
kBlue
};

enum class BColor {
kWhite,
kBlack,
kYellow
};

int main() {
if (AColor::kRed == BColor::kWhite) { // 编译失败
cout << "red == white" << endl;
}
return 0;
}

使用带有作用域的枚举类型后,对不同的枚举进行比较会导致编译失败,消除潜在bug,同时带作用域的枚举类型可以选择底层类型,默认是int,可以改成char等别的类型。

1
2
3
4
5
enum class AColor : char {
kRed,
kGreen,
kBlue
};

平时编程过程中使用枚举,一定要使用有作用域的枚举取代传统的枚举

非受限联合体

首先说明一下 POD类型

在C中,POD(Plain Old Data)类型没有用户自定义的构造函数、析构函数或虚函数,并且可以通过内存拷贝进行操作。C11引入了更严格的定义,将POD类型划分为三种:POD、POD Trivial、POD标准布局。这些类型通常适用于与C语言库交互、内存布局和二进制数据处理等情况

  1. 基本数据类型: 整数类型(如intcharlong)、浮点类型(如floatdouble)等是POD类型

    1
    2
    int x = 42;
    float y = 3.14;
  2. 结构体和类: 简单的结构体和类,只包含POD类型的成员且没有自定义构造函数、析构函数和虚函数,也可以是POD类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct Point {
    int x;
    int y;
    };

    class Rectangle {
    public:
    int width;
    int height;
    };
  3. C数组: C风格的数组也是POD类型

    1
    int array[5] = {1, 2, 3, 4, 5};

    std::vectorstd::array和动态内存分配 就不是

  4. 联合体: 简单的联合体也可以是POD类型,但要注意成员之间的内存布局

    1
    2
    3
    4
    union Data {
    int intValue;
    float floatValue;
    };
  5. 枚举: 枚举类型在某些情况下可以被视为POD类型,但也可能受到枚举的底层类型和使用方式的影响

    1
    enum Color { Red, Green, Blue };

    比如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    //1 包含成员函数
    enum class Color {
    Red,
    Green,
    Blue
    // 无法包含成员函数,否则不是POD类型
    // void print() {}
    };

    //2. 包含虚函数
    enum class Shape {
    Circle,
    Square
    // 无法包含虚函数,否则不是POD类型
    // virtual void draw() {}
    };

    //3. 继承
    class Base {
    public:
    int x;
    };

    enum class Derived : int, Base { // 不是POD类型,因为继承了Base类
    A,
    B
    };

大体上可以理解为对象可以直接memcpy的类型

c11之前union中数据成员的类型不允许有非POD类型,而这个限制在c11被取消,允许数据成员类型有非POD类型

1
2
3
4
5
6
7
8
9
struct A {
int a;
int *b;
};

union U {
A a; // 非POD类型 c++11之前不可以这样定义联合体
int b;
};

sizeof

c++11中sizeof可以用的类的数据成员上,看代码:

c++11前:

1
2
3
4
5
6
7
8
9
10
struct A {
int data[10];
int a;
};

int main() {
A a;
cout << "size " << sizeof(a.data) << endl;
return 0;
}

c++11后:

1
2
3
4
5
6
7
8
9
struct A {
int data[10];
int a;
};

int main() {
cout << "size " << sizeof(A::data) << endl;
return 0;
}

想知道类中数据成员的大小在c++11中是不是方便了许多,而不需要定义一个对象,在计算对象的成员大小

assertion

c++11引入static_assert声明,在编译期间检查,如果第一个参数值为false,则打印message,编译失败。

1
static_assert(true/false, message);

自定义字面量

c11可以自定义字面量,平时c中都或多或少使用过chrono中的时间,例如:

1
2
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 100ms
std::this_thread::sleep_for(std::chrono::seconds(100)); // 100s

其实没必要这么麻烦,也可以这么写:

1
2
std::this_thread::sleep_for(100ms); // c++14里可以这么使用,这里只是举个自定义字面量使用的例子
std::this_thread::sleep_for(100s);

这就是自定义字面量的使用,示例如下:

1
2
3
4
5
6
7
8
struct mytype {
unsigned long long value;
};
constexpr mytype operator"" _mytype ( unsigned long long n ) {
return mytype{n};
}
mytype mm = 123_mytype;
cout << mm.value << endl;

基础数值类型

c++11新增了几种数据类型:long longchar16_tchar32_t

long long 这是一种整数类型,用于表示更大范围的整数值。在某些平台上,long long 的范围要比传统的 intlong 类型更大,长度至少具有64位。

1
long long bigNumber = 123456789012345LL; // 后缀 LL 表示 long long 类型

char16_tchar32_t 这些是字符类型,用于表示更宽字符集的字符。在国际化和 Unicode 支持方面,它们非常有用,可以用来存储更多种类的字符

1
2
char16_t unicodeChar16 = u'\u03A9'; // 使用 u 前缀表示 char16_t 类型 至少具有16位
char32_t unicodeChar32 = U'\U0001F60A'; // 使用 U 前缀表示 char32_t 类型 至少具有32位

计算实际长度

1
2
3
4
5
6
7
8
9
#include <iostream>

int main() {
std::cout << "Size of long long: " << sizeof(long long) << " bytes" << std::endl;
std::cout << "Size of char16_t: " << sizeof(char16_t) << " bytes" << std::endl;
std::cout << "Size of char32_t: " << sizeof(char32_t) << " bytes" << std::endl;

return 0;
}

不同的编译器和平台可能会有不同的结果

正则表达式

c++11引入了regex库更好的支持正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <iterator>
#include <regex>
#include <string>

int main() {
std::string s = "I know, I'll use2 regular expressions.";
// 忽略大小写
std::regex self_regex("REGULAR EXPRESSIONS", std::regex_constants::icase);
if (std::regex_search(s, self_regex)) {
std::cout << "Text contains the phrase 'regular expressions'\n";
}

std::regex word_regex("(\\w+)"); // 匹配字母数字等字符
auto words_begin = std::sregex_iterator(s.begin(), s.end(), word_regex);
auto words_end = std::sregex_iterator();

std::cout << "Found " << std::distance(words_begin, words_end) << " words\n";

const int N = 6;
std::cout << "Words longer than " << N << " characters:\n";
for (std::sregex_iterator i = words_begin; i != words_end; ++i) {
std::smatch match = *i;
std::string match_str = match.str();
if (match_str.size() > N) {
std::cout << " " << match_str << '\n';
}
}

std::regex long_word_regex("(\\w{7,})");
// 超过7个字符的单词用[]包围
std::string new_s = std::regex_replace(s, long_word_regex, "[$&]");
std::cout << new_s << '\n';
}

chrono

c++11关于时间引入了chrono库,用于提供时间处理和时钟操作的支持

  • duration 用于表示时间间隔,例如秒、毫秒、微秒等
  • time_point 用于表示特定时间点
  • clocks 于获取时间信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <chrono>

int main() {
// 获取当前时间点
auto start = std::chrono::high_resolution_clock::now();

// 执行一些耗时操作
for (int i = 0; i < 1000000; ++i) {
// Do something
}

// 获取当前时间点
auto end = std::chrono::high_resolution_clock::now();

// 计算时间差
std::chrono::duration<double> duration = end - start;

// 输出执行时间
std::cout << "Time taken: " << duration.count() << " seconds" << std::endl;

return 0;
}

//Time taken: 0.0013827 seconds

参考链接

  1. https://github.com/0voice/cpp_new_features/blob/main/吐血整理:C%2B%2B11新特性.md
  2. https://blog.csdn.net/wangshubo1989/article/details/49134235