C++ 11

1.1字符串原始字面量

为了解决字符的转义和续行。 语法: “tag()tag” 注意 tag必须一样。

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

// 原始字符串就是为了解决c/c++字符的转义和续行
// R"tag(字符串)tag"

int main()
{
string str1 = "lichermionex\r\n\t111";
string str2 = R"(lichermionex\r\n\t111)";
string str3 = R"name(lichermionex\r\n\t111)name";

cout << "str1 : " << str1 << endl;
cout << "str1 : " << str2 << endl;
cout << "str1 : " << str3 << endl;
return 0;
}

运行结果

1
2
3
4
str1 : lichermionex
111
str1 : lichermionex\r\n\t111
str1 : lichermionex\r\n\t111

1.2 Long Long

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
#include <iostream>
using namespace std;

// long long c++11引入的

int main()
{
// 1有符号
long long num1 = 100LL;
//long long int
//signed long long
//signed long long int

// 2无符号
unsigned long long num2 = 100ULL; // 建议大写
unsigned long long int num22 = 100ULL; // 建议大写

//内存大小必须最少八个字节
cout << "long long max:" << LLONG_MAX << endl;
cout << "long long min:" << LLONG_MIN << endl;
cout << "unsigned long long max:" << ULLONG_MAX << endl;

// char short int long longlong
// unsigned: char short int long longlong

// 低等级到高等级的转换
char c = 6;
long long d = c;

// 低等级到高等级的转换

unsigned long long aa = -1; // 有符号到无符号的
cout << aa << endl;

return 0;
}

三个关键的宏:LLONG_MAX, LLONG_MIN, ULLONG_MAX。

补充知识: 整型家族的宏值

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

int main()
{
cout << "SHRT_MAX:" << SHRT_MAX << endl;
cout << "SHRT_MIN:" << SHRT_MIN << endl;
cout << "USHRT_MAX:" << USHRT_MAX << endl << endl;

cout << "INT_MAX:" << INT_MAX << endl;
cout << "INT_MIN:" << INT_MIN << endl;
cout << "UINT_MAX:" << UINT_MAX << endl <<endl;

cout << "LONG_MAX:" << LONG_MAX << endl;
cout << "LONG_MIN:" << LONG_MIN << endl;
cout << "ULONG_MAX:" << ULONG_MAX << endl;

cout << "LLONG_MAX:" << LLONG_MAX << endl;
cout << "LLONG_MIN:" << LLONG_MIN << endl;
cout << "ULLONG_MIN:" << ULLONG_MAX << endl;

cout << "CHAR_BIT:" << CHAR_BIT << endl;
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SHRT_MAX:32767
SHRT_MIN:-32768
USHRT_MAX:65535

INT_MAX:2147483647
INT_MIN:-2147483648
UINT_MAX:4294967295

LONG_MAX:2147483647
LONG_MIN:-2147483648
ULONG_MAX:4294967295
LLONG_MAX:9223372036854775807
LLONG_MIN:-9223372036854775808
ULLONG_MIN:18446744073709551615
CHAR_BIT:8

1.3 初始化列表

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
#include <iostream>	
#include <string>
using namespace std;

class base
{
public:
base(int num)
:a(num)
{}

// 非静态属性
int a = 2;
double b = 2.3;
char ch = 'c';

// 初始化列表
int a2{3};
double b2{ 2.4 };
char ch2{'c'};
string str{ "licher" };

// 静态属性
static int c1;
static const double c2;
static const char* c3;

void print() const
{
cout << "a " << a << endl;
}
};

// 静态属性,只能在类外初始化
int base::c1 = 22;
const double base::c2 = 22.5;
const char* base::c3 = "lic";

int main()
{
base b{ 1 };
b.print();
return 0;
}

1.4 final 和 override

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
#include <iostream>
using namespace std;

// final:修饰虚函数。此虚函数不能够被重写了。 写在虚函数的后面
// final:修饰对象,此对象不能够被继承了。 写在类的后面

class base
{
public:
virtual void test()
{
cout << "base" << endl;
}
};

class child final : public base // 修饰对象,此对象不能够被继承下去了的
{
public:
void test() final override // 此虚函数不能够被从写了。
{
cout << "child" << endl;
}
};

int main()
{

child c;
c.test();

return 0;
}

final 修饰虚函数,此虚函数被继承下去后不能够被重写了。

final 修饰对象,此对象不能够被继承了。

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
#include <iostream>
using namespace std;

// 虚函数一旦为虚,后续整个继承链中始终为虚;override 不会取消 virtual,
// 只有 final 可以禁止继续重写。

class base
{
public:
virtual void test()
{
cout << "base" << endl;
}
};

class child : public base
{
public:
void test() override // 检查虚函数是否被重写成功了的
{
cout << "child" << endl;
}
};

int main()
{
return 0;
}

检查继承下来的虚函数是否被重写了。

1.5右尖括号的对其

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
#include <iostream>
#include <map>
#include <string>
using namespace std;

template<class T>
class base
{
public:
void print(const T& t)
{
auto it = t.begin();
while (it != t.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
}
};

int main()
{
map<int, string> m;
m.insert(make_pair(1, "one"));
m.insert(make_pair(2, "two"));
m.insert(make_pair(3, "three"));

base<map<int, string>> c; // c11之前是 右移操作符
c.print(m);

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
#include <iostream>
#include <map>
#include <string>
using namespace std;

// 模板的模型类型
template<class T = long, class U = int>
void print(T t = 'A', U u = 'B')
{
cout << "t : " << t << " " << "u : " << u << endl;
}

int main()
{
// 可以自动推导,指定的实参推导
print('a', 'b');
print<char>('a', 'b');
print<int, char>('a', 'b');
print<char, int >('a', 'b');

// 使用默认的
print();

return 0;
}

运行结果

1
2
3
4
5
t : a u : b
t : a u : b
t : 97 u : b
t : a u : 98
t : 65 u : 66

没有指定需要推导的。默认的参数不能进行推导。

1.6静态断言

动态断言

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

// 动态断言:运行期间判断

char* createArray(int size)
{
assert(size > 0);
char* array = new char[size];
return array;
}

int main()
{
char* buf = createArray(0);
return 0;
}

编译期间不会报错,运行期间会报错的。

静态断言

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

// 静态断言:编译期间判断.

int main()
{
static_assert(sizeof(char*) == 8, "当前不是64位机器");
return 0;
}

编译期间进行常量表达式的检查。

1.7 noexcept

异常

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
#include <iostream>
#include <string>
using namespace std;

struct MyExcept
{
MyExcept(string str)
:msg(str)
{ }

string msg;
};

//void func() throw(int, double, MyExcept) // 异常类型限定
//void func() throw() // 不允许抛出异常
//void func() noexcept // 不允许抛出异常
void func()
{
try
{
//throw 1;
throw MyExcept("string exception !");
}
catch (int e) // 内置类型异常
{
cout << "throw value : " << e << endl;
}
catch (MyExcept e) // 自定义类型的异常
{
cout << "MyException : " << e.msg << endl;
}
}

int main()
{
func();
cout << "hello world" << endl;
return 0;
}

noexcept

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>
#include <string>
using namespace std;

struct MyExcept
{
MyExcept(string str)
:msg(str)
{ }

string msg;
};

void func() noexcept(sizeof(char*) == 4) // 32抛出
{
throw MyExcept("string exception !");
}

int main()
{
try
{
func();
}
catch (MyExcept e)
{
cout << e.msg << endl;
}

return 0;
}

noexcept不允许函数抛出异常的。 noexcept后面可以根常量表达式,用来控制函数是否抛出异常的。

1.8 数值转换成字符串

to_string函数重载

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
#include <iostream>
#include <string>
using namespace std;

int main()
{
int num1 = 1;
unsigned int num2 = 2;

long num3 = 3;
unsigned long num4 = 4;

long long num5 = 5;
unsigned long long num6 = 6;

float num7 = 7.0f;
double num8 = 2.3;
long double num9 = 4.5L;

string str1 = to_string(num1);
string str2 = to_string(num2);
string str3 = to_string(num3);
string str4 = to_string(num4);
string str5 = to_string(num5);
string str6 = to_string(num6);
string str7 = to_string(num7);
string str8 = to_string(num8);
string str9 = to_string(num9);

cout << "str1 : " << str1 << endl;
cout << "str2 : " << str2 << endl;
cout << "str3 : " << str3 << endl;
cout << "str4 : " << str4 << endl;
cout << "str5 : " << str5 << endl;
cout << "str6 : " << str6 << endl;
cout << "str7 : " << str7 << endl;
cout << "str8 : " << str8 << endl;
cout << "str9 : " << str9 << endl;

return 0;
}

运行结果

1
2
3
4
5
6
7
8
9
str1 : 1
str2 : 2
str3 : 3
str4 : 4
str5 : 5
str6 : 6
str7 : 7.000000
str8 : 2.300000
str9 : 4.500000

一共九个函数重载。int, long, long long, float, double, long double

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
#include <iostream>
#include <string>
#include <stdexcept>
#include <iomanip>
using namespace std;

int main()
{
// 1. 整数转换
int i = stoi("123");
long l = stol("1234567890");
long long ll = stoll("1234567890123456789");
unsigned long ul = stoul("4000000000");
unsigned long long ull = stoull("18446744073709551615");

cout << "i = " << i << '\n';
cout << "l = " << l << '\n';
cout << "ll = " << ll << '\n';
cout << "ul = " << ul << '\n';
cout << "ull = " << ull << '\n';

// 2. 使用 pos 查看转换停在哪里
string s = "123abc";
size_t pos = 0;
int n = stoi(s, &pos);

cout << "n = " << n << ", pos = " << pos
<< ", rest = " << s.substr(pos) << '\n';

// 3. 指定进制:16 进制、2 进制、自动识别
cout << stoi("ff", nullptr, 16) << '\n'; // 255
cout << stoi("1010", nullptr, 2) << '\n'; // 10
cout << stoi("0x1A", nullptr, 0) << '\n'; // 26

// 4. 浮点转换
float f = stof("3.14");
double d = stod("3.1415926535");
long double ld = stold("3.14159265358979323846");

cout << setprecision(20);
cout << "f = " << f << '\n';
cout << "d = " << d << '\n';
cout << "ld = " << ld << '\n';

return 0;
}

一共八个,int, long, long long, float ,double, long double 。 字符串,错误位置,进制

2.1 auto

基本的使用方法

使用:auto 变量名 = 变量

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
#include <iostream>
using namespace std;

int main()
{
int num{ 44 };

// 引用类型
int& p1 = num; // int&
const int& p2 = num; // const int&
volatile int& p3 = num; // volatile int&
volatile const int& p4 = num; // const volatile int&

// 指针类型
int* ptr1 = &num; // int*
const int* ptr2 = &num; // const int* (底层 const)
volatile int* ptr3 = &num; // volatile int* (底层 volatile)
volatile const int* ptr4 = &num; // const volatile int*(底层 const volatile)

// auto 推导(按值,丢弃顶层 cv,引用被去除)
auto ref_p1 = p1; // int (p1 是 int&,auto 去引用得 int)
auto ref_p2 = p2; // int (const 被丢弃,因为顶层 const)
auto ref_p3 = p3; // int (volatile 被丢弃)
auto ref_p4 = p4; // int (const volatile 都被丢弃)

// auto& 推导(保留引用绑定对象的 cv)
auto& ref_p12 = p1; // int&
auto& ref_p22 = p2; // const int&
auto& ref_p32 = p3; // volatile int&
auto& ref_p42 = p4; // const volatile int&

auto ref_ptr1 = ptr1; // int* (拷贝指针,值本身)
auto ref_ptr4 = ptr2; // const int* (底层 const 保留)
auto ref_ptr3 = ptr3; // volatile int* (底层 volatile 保留)
auto ref_ptr44 = ptr4; // const volatile int*(底层 cv 保留)

// auto* 推导(显式要求指针,推导所指类型,保留底层 cv)
auto* ref_ptr12 = ptr1; // int*
auto* ref_ptr22 = ptr2; // const int*
auto* ref_ptr32 = ptr3; // volatile int*
auto* ref_ptr42 = ptr4; // const volatile int*

return 0;
}

只有指针和引用 变量的const和volatil会被保留的

限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string>
#include <stdexcept>
#include <iomanip>
using namespace std;

// void func(auto a, auto b); 函数参数
// 类的非静态成员属性
// 定义数组
// 模板类型

int main()
{
return 0;
}

应用场景

STL遍历时候的迭代器

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
#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

void printMap()
{
map<int, string> m;
m.insert(make_pair(1, "one"));
m.insert(make_pair(2, "two"));
m.insert(make_pair(3, "three"));
m.insert(make_pair(4, "four"));
m.insert(make_pair(5, "five"));

//map<int, string>::iterator it = m.begin();
auto it = m.begin();
while (it != m.end())
{
cout << it->first << " : " << it->second << endl;
++it;
}
}

void printVector()
{
vector<char> v{ 'v', 'e', 'c', 't', 'o', 'r' };
for (auto it = v.begin(); it != v.end(); ++it)
{
cout << *it << endl;
}
}

int main()
{
printMap();
printVector();
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
32
33
34
35
36
37
38
#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

class Base1
{
public:
static int get()
{
return 22;
}
};

class Base2
{
public:
static string get()
{
return "hello world";
}
};

template<class T>
void func(void)
{
auto ret = T::get();
cout << "ret : " << ret << endl;
}

int main()
{
func<Base1>();
func<Base2>();

return 0;
}

2.2 decltype

基本使用

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
#include <iostream>
using namespace std;

int main()
{
int num{ 44 };

int& p1 = num; // int&
const int& p2 = num; // const int&
volatile int& p3 = num; // volatile int&
volatile const int& p4 = num; // const volatile int&

int* ptr1 = &num; // int*
const int* ptr2 = &num; // const int*
volatile int* ptr3 = &num; // volatile int*
volatile const int* ptr4 = &num; // const volatile int*

// ===== decltype(变量名):得到声明类型,保留引用和 cv =====
decltype(p1) d_p1 = p1; // int&
decltype(p2) d_p2 = p2; // const int&
decltype(p3) d_p3 = p3; // volatile int&
decltype(p4) d_p4 = p4; // const volatile int&

// ===== decltype(变量名) 对指针:得到指针类型本身(含底层 cv)=====
decltype(ptr1) d_ptr1 = ptr1; // int*
decltype(ptr2) d_ptr2 = ptr2; // const int*
decltype(ptr3) d_ptr3 = ptr3; // volatile int*
decltype(ptr4) d_ptr4 = ptr4; // const volatile int*

// ===== decltype((expr)):加括号,左值表达式 → 引用类型 =====
decltype((p1)) e_p1 = p1; // int&
decltype((p2)) e_p2 = p2; // const int&
decltype((p3)) e_p3 = p3; // volatile int&
decltype((p4)) e_p4 = p4; // const volatile int&

// 对指针加括号:ptr 本身是左值 → 得到指针的引用
decltype((ptr1)) e_ptr1 = ptr1; // int*&
decltype((ptr2)) e_ptr2 = ptr2; // const int*&
decltype((ptr3)) e_ptr3 = ptr3; // volatile int*&
decltype((ptr4)) e_ptr4 = ptr4; // const volatile int*&

// ===== 对比:若用 decltype 配合 auto 原代码中的按值拷贝 =====
// auto ref_p2 = p2; → int
// decltype(p2) → const int& (完全不同!)
// 想要"按值"效果需去掉引用:std::remove_reference_t<decltype(p2)>

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
32
33
#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

class Test
{
public:
int a;
string str;
};

// decltype (表达式) :根据表达式推导出类型,不会计算表达式的

int main()
{
int a = 22;
decltype(a) b = 99; // int b
decltype(a + 3.14) c = 55.3; // double c
decltype(a + b * c) d = 1.12; // double d

int num = 10;
const int& numm2 = num;

decltype(num) num3 = 55; // int num3;
decltype(numm2) num4 = num; // const int num4;

decltype(Test::a) num6 = 555; // int num6
decltype(Test::str) str = "licher"; // string str

return 0;
}

decltype(表达式) 变量名 = 初始值 。

函数返回值的使用

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
#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

// decltype (表达式) :根据表达式推导出类型,不会计算表达式的
// 函数返回值

int func_int(){}
int& func_int_r(){}
int&& func_int_rr(){}

const int func_const_int() {}
const int& func_const_int_r() {}
const int&& func_const_int_rr(){}

int main()
{
decltype(func_int()) a = 22; // int a;
decltype(func_int_r()) b = a; // int& a;
decltype(func_int_rr()) b = 33; // int&& a;
// 字面量:纯右值
decltype(func_const_int()) c = 22; // 纯粹的右值,直接去掉const, 就是 int c = 22;
decltype(func_const_int_r()) d = a; // const int& d = a;
decltype(func_const_int_rr()) e = 22; // cons int&& e = 22;

return 0;
}

纯右值,字面量的时候就是一个常量。 去掉const

被()包围的时候或者是一个左值, decltype推导的时候是引用, 不能去掉 const和volatile限定符。

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
#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

// decltype (表达式) :根据表达式推导出类型,不会计算表达式的
// 左值被括号包围,const和volatile不能去掉

class Test
{
public:
int a;
string str;
const static int b = 22;
};

int main()
{
const Test test;

decltype(test.a) a = 22; // int a = 22;
decltype(test.b) b = 22; // const int b = 22;

// const int& a2 = a;
decltype((test.a)) a2 = a; // 带上const因为定义的时候,是const对象。c

int n = 0;
int m = 0;
decltype(n + m) c = 0; // 纯右值,字面量, int c = 0;

decltype(n = n + m) d = n; // 左值 int& d = n;

return 0;
}

decltype的应用

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>
#include <string>
#include <list>
#include <map>
using namespace std;

// decltype (表达式) :根据表达式推导出类型,不会计算表达式的
// 左值被括号包围,const和volatile不能去掉

// 泛型编程:遍历容器

template<class T>
class Container
{
public:
void print(T& t)
{
for (_it = t.begin(); _it != t.end(); ++_it)
{
cout << "value:" << *_it << endl;
}
}

private:
decltype(T().begin()) _it; // 得到迭代器的类型
};

int main()
{
list<int> ls{ 1, 2,4,5,6,7,8, };

Container<list<int>> c;
c.print(ls);

Container<const list<int>> c1;
c1.print(ls);

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
#include <iostream>
#include <vector>
#include <string>
#include <list>
#include <map>
using namespace std;

// decltype (表达式) :根据表达式推导出类型,不会计算表达式的
// 左值被括号包围,const和volatile不能去掉

// **返回值类型后置**
template<class T, class U>
auto add(T t, U u) -> decltype(t + u) // auto推导的是decltype(t+u);
{
return t + u;
}

int main()
{
cout << "ret1 :" << add(1, 22.5) << endl;
cout << "ret2 :" << add<int, double>(1, 22.5) << endl;

return 0;
}

2.3范围for

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <vector>
#include <string>
#include <list>
#include <map>
using namespace std;

int main()
{
vector<int> v{ 1, 3, 4, 5, 6, 7,6 };
for (const auto& e : v)
{
cout << "e : " << e << 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
#include <iostream>
#include <vector>
#include <string>
#include <list>
#include <map>
using namespace std;

int main()
{
map<int, string> m;
m.insert(make_pair(1, "one"));
m.insert(make_pair(2, "two"));
m.insert(make_pair(3, "three"));
m.insert(make_pair(4, "four"));

for (auto& e : m) // 这是一个对象
{
cout << e.first << ":" << e.second << endl;
}

for (auto it = m.begin(); it != m.end(); ++it) // 这是迭代器,类似指针。
{
cout << it->first << ":" << it->second << endl;
}

return 0;
}

set默认只读的。

2.4 nullptr

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
#include <iostream>
using namespace std;

void func(char* p)
{
cout << "func(char*)" << endl;
}

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

int main()
{
func(NULL); // #define NULL 0
func(0);

char* p1 = nullptr;
char* p2 = NULL;

func(p1);
func(p2);

void* ptr1 = NULL;
// int* ptr2 = ptr1; void*不能够隐士的转换为int*
int* ptr2 = (int*)ptr1; // 需要显示的转换

// 自动转换为任意类型的指针
void* ptr3 = nullptr;
double* ptr3 = nullptr;
char* ptr3 = nullptr;

return 0;
}

C++的NULL就是定义的宏:0。

nullptr c++的万能指针。初始化的时候可以定义为任何类型的指针。

2.5 Lambda函数

1
2
3
4
5
6
7
8
9
// [capture] (params) opt->ret(body;};
[]:不捕捉任何变量
[&]:引用的方式捕捉
[=]:捕获变量,作为副本在函数中使用。值拷贝

没有参数,可以省略

opt选项:mutable, exception
mutable:修改拷贝的临时对象
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
#include <iostream>
using namespace std;

// 匿名函数
// [capture] (params) opt->ret(body;};

void func(int x, int y)
{
int a = 1;
int b = 2;
[=, &x]()mutable
{
int c = a;
int d = b;
b++; // 修改的临时对象,加了mutable
cout << "b : " << b << endl;
}(); // 后面添加()就是调用匿名函数的

cout << "b : " << b << endl;
}

int main()
{
func(1, 2);

return 0;
}

lambda本身:仿函数。

捕获列表就是给匿名对象增加属性, mutable就是去掉函数的const。

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 <functional>
using namespace std;

// 匿名函数
// [capture] (params) opt->ret(body;};

void func(int x, int y)
{
int a = 10;
int b = 20;
using ptr = void(*)(int);

// 函数指针
ptr p1 = [](int x)
{
cout << "x : " << x << endl;
};
p1(22);

// 函数包装器
function<void(int x)> f1 = [=](int x)
{
cout << "x : " << x << endl;
};
f1(33);

// 绑定器
function<void(int x)> f2 = bind([=](int x)
{
cout << "x : " << x << endl;
}, placeholders::_1);
f2(44);
}

int main()
{
func(1, 2);
return 0;
}

3.1 constexpr

constexpr修饰常量数据

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
#include <iostream>
#include <functional>
using namespace std;

// constexpr:常量表达式,函数的返回值是常量。

void func()
{
const int a = 20; // 只读
int arr[a] = { 0 }; // 常量
}

struct T { int a; };

int main()
{
// 常量表达式:编译期间就知道了。
// 非常量表达式:程序运行期间才知道的。
// 编译器怎么知道常量表达式:constexpr.
const int i = 1314; // 常量表达式
const int j = i + 1; // 常量表达式

// 编译期间我们就知道了结果
constexpr int t1 = 44;
constexpr int t2 = t1 + 33;

// 常量结构体,类对象.创建对象的时候用constexpr.
constexpr T t1{ 222 };
constexpr T t2{ 333 };

return 0;
}

constexpr修饰函数

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;

// 修饰函数
// 1.函数返回的时候常量表达式。
constexpr int func()
{
return 0;
// return 变量不符合的。
}

// 2.声明,定义,调用。
// 3.整个函数只能出现常量表达式语句。using,typedef,static_assert return除外。

int main()
{
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
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <functional>
using namespace std;

// 修饰函数
// 1.函数返回的时候常量表达式。
constexpr int func()
{
return 0;
// return 变量不符合的。
}

// 2.声明,定义,调用。
// 3.整个函数只能出现常量表达式语句。using,typedef,static_assert return除外。

// 模板函数根据实际情况进行判断的。
struct Person
{
const char* name;
int age;
};

template<class T>
constexpr T display(T t)
{
return t;
}

int main()
{
// 普通函数
Person p{ "lichermionex", 22 };
Person p1 = display<Person>(p);

// 常量表达式
constexpr int ret = display<int>(22);

constexpr Person p3{ "lichermionex", 22 };
constexpr Person p4 = display<Person>(p3);

return 0;
}

修饰构造函数,构造函数必须是空的。只能写初始化列表里面的。

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

struct T
{
constexpr T(int a, int b, const char* p)
:_a(a)
,_b(b)
,_p(p)
{ }
int _a;
int _b;
const char* _p;
};

int main()
{
// t就是一个常量对象的。
constexpr T t(22, 33, "lic");

return 0;
}

constexpr关键字就是为了能够在编译期间判断常量的值,减少在运行期间的判断。提高效率。

3.2 using

基本变量和函数指针。

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
#include <iostream>
using namespace std; // 最常用的using

// using使用命名空间的
// using使用隐藏的函数

// 定义类型的别名,类型的别名

typedef int(*func_ptr1)(int, string);
using func_ptr2 = int(*)(int, string);

int func(int a, string b)
{
cout << "a : " << a << " " << "b : " << b << endl;
return 0;
}

int main()
{
// 变量
typedef int t;
t a = 10; // 都是int
t b = 10;

using t2 = int;
t2 a1 = 10;
t2 b1 = 10;

// 函数
func_ptr1 f1 = func;
func_ptr2 f2 = func;

f1(1, "lic1");
f2(2, "lic2");

// 先解引用
(*f1)(3, "lic3");
(*f2)(4, "lic4");

return 0;
}

**模板使用 using **

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>
#include <map>
using namespace std; // 最常用的using

// 模板定义别名
// map int-int, int-double, int-string

// 麻烦
template<class T>
struct MyMap
{
typedef map<int, T> mapType;
};

template<class T>
using MMap = map<int, T>; // 清爽

int main()
{
// typedef
MyMap<int>::mapType m1{ {1,1}, {2, 2}, {3,3} };
MyMap<double>::mapType m2{ {1,1.1}, {2,2.2}, {3,3.3} };
MyMap<std::string>::mapType m3{ {1, "one"}, {2, "two"}, {3, "three"} };

// using
MMap<int> m4{ {1,1}, {2, 2}, {3,3} };
MMap<double> m5{ {1,1.1}, {2,2.2}, {3,3.3} };
MMap<std::string> m6{ {1, "one"}, {2, "two"}, {3, "three"} };

return 0;
}

3.3 委托构造

构造函数调用构造函数。不要形成闭环。尽量写在初始化列表里面。

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
#include <iostream>
using namespace std;

class Test
{
public:
Test(){}
Test(int max)
{
_max = max > 0 ? max : 100;
}

Test(int max, int min)
{
_max = max > 0 ? max : 100;
_min = min > 0 && min < max ? min : 1;
}

Test(int max, int min, int mid)
{
_max = max > 0 ? max : 100;
_min = min > 0 && min < max ? min : 1;
_mid = mid < max && mid > min ? mid : 50;
}
private:
int _max;
int _min;
int _mid;
};

// 委托构造:构造函数调用构造函数。在初始化列表里面调用自己的构造函数。
class Test2
{
public:
Test2() {}
Test2(int max)
{
_max = max > 0 ? max : 100;
}

Test2(int max, int min)
:Test2(max)
{
_min = min > 0 && min < max ? min : 1;
}

Test2(int max, int min, int mid)
:Test2(max, min)
{
_mid = mid < max && mid > min ? mid : 50;
}
private:
int _max;
int _min;
int _mid;
};

int main()
{
return 0;
}

构造函数调用构造函数。

3.4 继承构造函数

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
#include <iostream>
#include <string>
using namespace std;

class Base
{
public:
Base(int i)
:_i(i)
{}

Base(int i, int j)
:_i(i)
,_j(j)
{}

Base(int i, int j, int k)
:_i(i)
,_j(j)
,_k(k)
{}

void func(int i)
{
cout << "func(int i)" << "i " << i << endl;
}
void func(int i, string str)
{
cout << "func(int i, string str) " << "i : " << i << "str : " << str << endl;
}


public:
int _i;
int _j;
int _k;
};

class Child : public Base
{
public:
using Base::Base; // 使用基类的构造函数。
using Base::func; //

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


int main()
{
Child c{ 1, 2, 3 };
cout << "_i : " << c._i << endl;
cout << "_j : " << c._j << endl;
cout << "_k : " << c._k << endl;

c.func(1);
c.func(2, "lic");
c.func();

return 0;
}

继承基类的构造函数

继承基类的隐藏函数,或者隐藏属性。

3.5 初始化列表

统一初始化 {}

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
#include <iostream>
#include <string>
using namespace std;

// 初始化列表,统一初始化。

class Person
{
public:
Person(int age)
:_age(age)
{ }

Person(int age, string name)
:_age(age)
,_name(name)
{ }
private:
int _age;
string _name;
};

int main()
{
// 对象
Person p1(22); // 调用构造函数
Person p2 = 33; // 隐士类型转换

Person p3{ 44 };
Person p4 ={ 44 };

// 基本类型的初始化
int a1{ 10 };
int a2={ 10 };

char ch1{ 2 };
char ch2={ 2 };

// 初始化数组
int arr1[]{ 1,2,3 };
int arr2[]={ 1,2,3 };

// 堆内存 new出来的堆空间
int* p1 = new int{ 44 };
int* p2 = new int[3]{1, 3, 4};

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <string>
using namespace std;

// T1不是聚合体,受保护的成员属性
struct T1
{
int x;
long y;
protected:
int z;
};

// T2是聚合体,静态属性属于整个类,不需要初始化列表进行初始化的
struct T2
{
int x;
long y;
protected:
static int z;
};

struct T3
{
int a = 4;
int arr[3]{ 1,2,3 };
int c;
};

int T2::z = 44;

int main()
{
// 聚合体类型,直接初始化。
// 1.数组
int x1[]{ 1,2,3,4 };
int x2[]={ 1,2,3,4 };

// 2.类
// 1.无自定义的构造函数 2.无私有或保护的非静态数据成员。3无基类和虚函数。

//T1 t{ 1,2,3 }; error
T2 t{ 1,2}; // 正确的

T3 t3{ 4, {4,5,6}, 7 }; // c++14支持了。对象里面有=或者{}初始化过的数据。

return 0;
}

initializer_list

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 <string>
#include <initializer_list>
using namespace std;

void func(initializer_list<int> ls) // 一般作为函数的参数
{
initializer_list<int>::iterator it = ls.begin();
while (it != ls.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}

void func(initializer_list<char>& ls)
{
initializer_list<char>::iterator it = ls.begin();
while (it != ls.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}

int main()
{
func({ 1,2,3,4,5,6,7,8,9,10 }); // {}接收任意多个类型一样的参数
func({ 'a', 'b', 'c', 'd', 'e' }); // {} 整体算一个参数

return 0;
}

3.7 可调用对象

类似函数指针一样,直接对象,然后就是需要的参数。

3.7.1 可调用对象

1.函数指针

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

// 1.函数指针
void print(int num, const string& name)
{
cout << "num : " << num << " : " << "name : " << name << endl;
}
using funcptr = void(*)(int, const string&); // 函数指针类型

int main()
{
funcptr p = print;
p(10, "lichermionex"); // 函数指针
return 0;
}

2.仿函数

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

class Test
{
public:
// 重载 operator()
void operator()(const string& msg)
{
cout << "msg : " << msg << endl;
}
};

int main()
{
Test t;
t("lichermionex"); // 仿函数
return 0;
}

3将类对象转换为函数指针

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
#include <iostream>
#include <string>
using namespace std;

using funcptr = void(*)(int, const string&); // 函数指针类型

class Test
{
public:

// 类对象转换为函数指针。
operator funcptr()
{
return world;
}

static void world(int a, const string& s) // 类
{
cout << "a:" << a << endl;
cout << "s:" << s << endl;
}
};
// 4.类的函数指针或者类成员的指针

int main()
{
Test tt;
tt(18, "lichermionex"); // 类对象转换为函数指针
return 0;
}

4.类的函数指针或者类成员的指针

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
#include <iostream>
#include <string>
using namespace std;

using funcptr = void(*)(int, const string&); // 函数指针类型

class Test
{
public:
void hello(int a, const string& s) // 对象
{
cout << "a:" << a << endl;
cout << "s:" << s << endl;
}

static void world(int a, const string& s) // 类
{
cout << "a:" << a << endl;
cout << "s:" << s << endl;
}

int _id;
};
// 4.类的函数指针或者类成员的指针

int main()
{
// 类的函数指针
funcptr f = Test::world; // 静态方法,没有this指针、

// 类非静态方法,需要指明作用域指针类型。
using fptr = void(Test::*)(int, const string&);
fptr f1 = &Test::hello;
Test ttt;
(ttt.*f1)(22, "licher"); // 调用的时候,主需要先解引用,然后再调用。

// 类的成员指针(变量),需要指针指针类型的作用域
using ptr1 = int Test::*;
ptr1 pt = &Test::_id;
ttt.*pt = 555; // 属性

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
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
#include <iostream>
#include <string>
using namespace std;

using funcPtr = void(*)(int, const string&);
using funcPtr1 = void(*)(int);

void print(int id, const string& str)
{
cout << "id : " << id << " " << "str : " << str << endl;
}

class test
{
public:
void operator()(int id, const string& str)
{
cout << "id : " << id << " " << "str : " << str << endl;
}

static void hello(int id, const string& str)
{
cout << "id : " << id << " " << "str : " << str << endl;
}

void world(int id, const string& str)
{
cout << "id : " << id << " " << "str : " << str << endl;
}

static void print_int(int x)
{
cout << "print x : " << x << endl;
}

operator funcPtr1()
{
return print_int;
}
};

int main()
{
// 1.函数指针
funcPtr ptr1 = print;
print(1, "lic1");

// 2.仿函数,属于某个对象,需要创建对象的
test t;
t(2, "lic2");

// 3.类的静态函数,属于整个类,不需要创建对象的
funcPtr ptr2 = test::hello;
ptr2(3, "lic3");

// 4.类的非静态函数,属于某个
test ttt;
using fptr = void(test::*)(int, const string&);
fptr ptr3 = &test::world;
(ttt.*ptr3)(4, "lic4");

// 5.类对象返回函数指针的。
ttt(555);

return 0;
}

3.7.2函数包装器

std::function<返回值(参数列表)> diy_name = 可调用对象。

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
#include <iostream>
#include <string>
#include <functional>
using namespace std;

// 1.函数指针
void print(int num, const string& name)
{
cout << "num : " << num << " : " << "name : " << name << endl;
}
using funcptr = void(*)(int, const string&); // 函数指针类型

// 2.仿函数,重载了operator()
class Test
{
public:
// 重载
void operator()(const string& msg)
{
cout << "msg : " << msg << endl;
}

// 类对象转换为函数指针。
operator funcptr()
{
return world;
}

void hello(int a, const string& s) // 对象
{
cout << "a:" << a << endl;
cout << "s:" << s << endl;
}

static void world(int a, const string& s) // 类
{
cout << "a:" << a << endl;
cout << "s:" << s << endl;
}

int _id;
};
// 4.类的函数指针或者类成员的指针

int main()
{
// 1.普通函数
function<void(int, const string&)> f1 = print;
print(1, "lic1");

// 2.类的静态函数
function<void(int, const string&)> f2 = Test::world; // 注意指定作用域
f2(2, "lic2");

// 3.仿函数
Test t1;
function<void(const string&)> f3 = t1;
f3("lic333");

// 4.转换为函数指针的对象
Test t2;
function<void(int, const string&)> f4 = t2;
f4(4, "lic4444");

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
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
67
68
#include <iostream>
#include <string>
#include <functional>
using namespace std;

class A
{
public:
A(const function<void(int, string)>& callback)
:_callback(callback)
{ }

void notify(int id, string name)
{
_callback(id, name);
}

private:
function<void(int, string)> _callback;
};

void print(int id, string name)
{
cout << "id : " << id << endl;
cout << "string : " << name << endl;
}

using funcptr = void(*)(int, const string&); // 函数指针类型

// 2.仿函数,重载了operator()
class Test
{
public:
static void world(int a, const string& s) // 类
{
cout << "a:" << a << endl;
cout << "s:" << s << endl;
}

operator funcptr()
{
return world;
}

int _id;
};

int main()
{
//function<void(int, string)> f = print;
//A a(f);

// 基本函数
A a(print);
a.notify(11, "lic11");

// 仿函数
A b(Test::world);
b.notify(22, "lic22");

// 包装为函数指针的对象
Test tb;
function<void(int, const string&)> f4 = tb;
A c(tb);
c.notify(3, "lic33");

return 0;
}

3.7.3 绑定器

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
#include <iostream>
#include <string>
#include <functional>
using namespace std;

// 1.将函数和参数绑定在一起,形成仿函数
// 2.简化函数参数

// auto f = std::bind(可调用对象, 参数或者占位符)
// auto f = std::bind(类函数、成员地址, 对象, 参数或者占位符)

// placeholders::_1函数第一个参数
// placeholders是后面参数的顺序

void output(int x, int y)
{
cout << "x : " << x << endl;
cout << "y : " << y << endl;
}

int main()
{
bind(output, 1, 2)(); // 1,2
bind(output, placeholders::_1, 2)(1111); // 1111 2
bind(output, 2, placeholders::_1)(1111); // 2 1111

bind(output, 2, placeholders::_2)(10, 10); // 2 10
bind(output, placeholders::_1, placeholders::_2)(10, 11); // 10 11
bind(output, placeholders::_2, placeholders::_1)(10, 11); // 11 10

return 0;
}

直接结果

1
2
3
4
5
6
7
8
9
10
11
12
x : 1
y : 2
x : 1111
y : 2
x : 2
y : 1111
x : 2
y : 10
x : 10
y : 11
x : 11
y : 10

测试代码

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
#include <iostream>
#include <string>
#include <functional>
using namespace std;

// 1.将函数和参数绑定在一起,形成仿函数
// 2.简化函数参数

// auto f = std::bind(可调用对象, 参数或者占位符)
// auto f = std::bind(类函数、成员地址, 对象, 参数或者占位符)

// placeholders::_1函数第一个参数

void testFunc(int x, int y, const function<void(int, int)> & f)
{
if (x % 2 == 0)
{
f(x, y);
}
}

void output(int x, int y)
{
cout << "x : " << x << " " << "y : " << y << " " << "x + y : " << (x + y) << endl;
}

int main()
{
for (int i = 0; i < 10; ++i)
{
auto f1 = bind(output, i + 100, i + 200); // 这里的参数已经固定了的
testFunc(i, i, f1);

auto f2 = bind(output, placeholders::_1, placeholders::_2);
testFunc(i, i, f2);
}

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
32
33
34
35
36
37
38
39
#include <iostream>
#include <string>
#include <functional>
using namespace std;

// 1.将函数和参数绑定在一起,形成仿函数
// 2.简化函数参数

// auto f = std::bind(可调用对象, 参数或者占位符)
// auto f = std::bind(类函数、成员地址, 对象, 参数或者占位符)

// placeholders::_1函数第一个参数

class Test
{
public:
void output(int x, int y)
{
cout << "x : " << x << " y : " << y << endl;
}

int _number = 100;
};

int main()
{
// 成员函数绑定,成员函数属于对象,需要创建对象的示例。
Test t;
bind(&Test::output, &t, 1, 2)();
bind(&Test::output, &t, 1, placeholders::_2)(777,888);

// 类的成员变量,
auto f = bind(&Test::_number, &t);

f() = 777;
cout << f() << 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <string>
#include <functional>
using namespace std;

// 1.将函数和参数绑定在一起,形成仿函数
// 2.简化函数参数

// auto f = std::bind(可调用对象, 参数或者占位符)
// auto f = std::bind(类函数、成员地址, 对象, 参数或者占位符)

// placeholders::_1函数第一个参数

class Test
{
public:
void output(int x, int y)
{
cout << "x : " << x << " y : " << y << endl;
}

int _number = 100;
};

int main()
{
// 成员函数绑定,成员函数属于对象,需要创建对象的示例。
Test t;
bind(&Test::output, &t, 1, 2)();
bind(&Test::output, &t, 1, placeholders::_2)(777,888);

// 类的成员变量,
auto f = bind(&Test::_number, &t);

cout << f() << endl;

f() = 777;
cout << f() << endl;

// 包装一下方法
function<void(int, int)> ff = bind(&Test::output, &t, 520, placeholders::_2);
ff(0, 1314);

// 包装一下属性
function<int&(void)> fff = bind(&Test::_number, &t);
return 0;
}

3.8 右值引用

左值:loactor value。可以定位的值

右值:read value。只读值

左值:空间

右值:数据

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
#include <iostream>

// lvalue:loactor value,左值:定位值
// rvalue:read value 有值:只读值

// 左值引用,右值引用

int main()
{
// 左值
int a = 44;

// 左值引用
int& refa = a;

// 右值
44;
'a';
"abcd";

// 右值引用
int&& c = 44;

// 常量左值引用
const int& d = a;
const int& d2 = c; // 常量右值引用
const int& d3 = 44; // 这里会有隐式类型转换

// 常量右值引用
const int&& f = 44;

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
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
#include <iostream>
using namespace std;

class test
{
public:
test()
:_a(new int{ 22 })
{
cout << "test()" << endl;
cout << &_a << endl;
}

// 拷贝构造:防止浅拷贝,实现深拷贝
test(const test& t)
:_a(new int{ *t._a })
{
cout << &_a << endl;
cout << "test(const test& t)" << endl;
}

// 移动构造就是为了实现浅拷贝(有指针存在的话)。
// 复用这个临时对象的资源。
test(test&& t)
:_a(t._a)
{
t._a = nullptr;
cout << " test(test&& t)" << endl;
cout << &_a << endl;
}

~test()
{
cout << "~test()" << endl;
delete _a;
}

public:
int* _a;
};

test getobj()
{
test t; // 这里构造函数一次
return t;
}

int main()
{
// 右值引用:延长右值的生命周期。
// 得到一个临时对象,
test t = getobj(); // 这里一般拷贝构造一次。 但是存在移动构造,会调用拷贝构造。
test&& t1 = getobj(); // 临时对象,才会进行移动构造的调用

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
#include <iostream>
#include <string>
using namespace std;

// &&特性
// 1.通过右值推导:T&& 或者 auto&& 得到的是右值引用
// 2.

template<class T>
void f(T&& param); // 需要推导


template<class T>
void f1(const T&& param); // 都是右值


int main()
{
f(10); // 右值
int x = 10;
f(x); // 左值

f1(55); // f1一直都是右值

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
32
33
34
35
#include <iostream>
#include <string>
using namespace std;


int main()
{
int x = 1;
int y = 2;

auto&& v1 = x; // 左值
auto&& v2 = 222; // 右值

//decltype(x) && v3 = y; // error 右值引用赋了左值

int&& a1 = 44;
auto&& bb = a1; // 左值引用 int& b = a;
auto&& cc = 66; // 右值引用 int&& cc = 66;


int a2 = 5;
int& a3 = a2;
auto&& ccc = a3; // 左值引用 int& ccc = a3;
auto&& ccc1 = a2; // 左值引用 int& ccc1 = a3;

const int& s1 = 1000;
const int&& s2 = 1000;

auto&& dd = s1; // const int& dd = s1;
auto&& ee = s2; // const int& ee = s2;

const auto&& xxx = 5; // const int&& x = 5;

return 0;
}

通过右值推导 T&& 或者 auto&& 得到的是一个右值引用类型。 右值是具体的右值,不是变量。

const T&& 就是一个右值类型,不需要推导了的。

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
#include <iostream>
using namespace std;

void print(int& i)
{
cout << "left : " << i << endl;
}


void print(int&& i)
{
cout << "right : " << i << endl;
}

void forward(int&& k)
{
print(k);
}


int main()
{
int i = 55;
print(i);
print(44);
forward(444); // 这里是左值,在传递的时候。右值在传递的时候变成左值。

return 0;
}

move

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

int main()
{
// move:左值转换为右值。 所有权的转换。
list<string> ls{ {"lic1"}, {"lic2"}, {"lic3"} };
list<string> ls2 = move(ls);

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
32
33
34
35
36
37
38
#include <iostream>
#include <list>
#include <string>
using namespace std;

template<class T>
void print(T& t)
{
cout << "left value : " << t << endl;
}

template<class T>
void print(T&& t)
{
cout << "right value : " << t << endl;
}

template<class T>
void testforward(T&& v)
{
print(v);
print(move(v));
print(forward<T>(v));
}

int main()
{
// 当T为左值引用类型时 t将被转换为T类型的左值
// 当T不是左值引用类型时 t将被转换为T类型的右值

testforward<int>(520);

int num = 44;
testforward(num);


return 0;
}