std::function
把对象当作函数使用。
好处:运算符的参数个数不变,但通过数据成员可以使得函数功能“模板化”
一个std::function类型对象实例可以包装下列这几种可调用元素类型:函数、函数指针、类成员函数指针或任意类型的函数对象(例如定义了operator()操作并拥有函数闭包)
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 <string>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Time
{
public:
unsigned int Hour;
unsigned int Minute;
unsigned int Second;
};
class DisplayTime
{
public:
DisplayTime(unsigned int Min = 0, unsigned int Max = 24)
: m_Min(Min), m_Max(Max) {}
void operator()(const Time &t1)
{
if (t1.Hour >= m_Min && t1.Hour <= m_Max)
{
cout << t1.Hour << ":" << t1.Minute << ":" << t1.Second << endl;
}
}
private:
unsigned int m_Min;
unsigned int m_Max;
};
int main()
{
vector<Time> V{{1, 2, 3}, {2, 5, 6}, {1, 9, 0}};
for_each(V.begin(),V.end(),DisplayTime());
// 更加优雅的写法
// DisplayTime Display;
// for_each(V.begin(),V.end(),Display);
}
|
更多用法参考:https://en.cppreference.com/w/cpp/utility/functional/function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include <iostream>
#include <functional>
using namespace std;
int g_Minus(int i, int j)
{
return i - j;
}
int main()
{
function<int(int, int)> f = g_Minus;
cout << f(1, 2) << endl; // -1
return 1;
}
|
std::bind
https://thispointer.com/stdbind-tutorial-and-usage-details/
bind是这样一种机制,它可以预先把指定可调用实体的某些参数绑定到已有的变量,产生一个新的可调用实体。与std::function
的区别,可由以下代码意会:
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<functional>
#include<iostream>
using std::placeholders;
double cal(double a,double b,char op)
{
switch(op)
{
case '+':
return a+b;
case '-':
return a-b;
default:
return 0;
}
}
int main()
{
// 通过占位符,设置函数的不同固定参数,在传参时更加层次分明,可读性更强
auto plus_with_bind = std::bind(cal, _1, _2, '+');
std::cout << plus_with_bind(1,2);
std::function<double(double , double , char)> plus_with_func = cal;
// 可见传参时与std::bind的区别
std::cout << plus_with_func(1,2,'+');
}
|
tuple
C++ 17为元组的使用提供了极大的方便。
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <iostream>
#include <tuple>
std::tuple<int, double, std::string> f()
{
return std::make_tuple(1, 2.3, "456");
}
int main()
{
auto [x, y, z] = f();
std::cout << x << ", " << y << ", " << z << std::endl;
return 0;
}
|
变长参数
C++ 17中的折叠表达式
1
2
3
4
5
6
7
8
9
10
|
#include <iostream>
template <typename... T>
auto sum(T... t)
{
return (t + ...);
}
int main()
{
std::cout << sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << std::endl;
}
|
C++ 17中的变参模板展开
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include <iostream>
template <typename T0,typename... T>
auto print(T0 t0,T... t)
{
std::cout << t0 << std::endl;
if constexpr (sizeof...(t) > 0)
{
print(t...);
}
}
int main()
{
print("1",2,'3');
}
|
避免了以前递归函数的繁琐写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
template<typename T>//0个参数
void show_list(){}
template<typename T>//1个参数
void show_list(const T& value)
{
std::cout<<value<<std::endl;
}
template<typename T,typename... Args>//2个参数以上
void show_list(const T& value,const Args&... args)
{
std::cout<<value<<std::endl;
show_list(args...);
}
|
强转
dynamic_cast
多用于多态的转换。
用法:
dynamic_cast<type*>(e) //e是指针
dynamic_cast<type&>(e) //e是左值
dynamic_cast<type&&>(e)//e是右值
static_cast
多用于基本类型转换。
1
|
int a = static_cast<int>(120.34);
|
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
|
/**
* @file rtti.cpp
* @brief 在面向对象程序设计中,有时我们需要在运行时查询一个对象是否能作为某种多态类型使用。与Java的instanceof,以及C#的as、is运算符类似,C++提供了dynamic_cast函数用于动态转型。相比C风格的强制类型转换和C++ reinterpret_cast,dynamic_cast提供了类型安全检查,是一种基于能力查询(Capability Query)的转换,所以在多态类型间进行转换更提倡采用dynamic_cast
* @author 光城
* @version v1
* @date 2019-07-24
*/
// CPP program to illustrate
// // Run Time Type Identification
#include <iostream>
#include <typeinfo>
class B
{
public:
virtual void fun()
{
std::cout << "B virtual" << std::endl;
}
};
class D : public B
{
public:
virtual void fun() final
{
std::cout << "D virtual" << std::endl;
}
void foo()
{
std::cout << "D" << std::endl;
}
};
int main()
{
B *b = new D; // 向上转型
b->fun();
D *d = dynamic_cast<D *>(b); // 向下转型
d->fun();
d->foo();
}
|
extern “C”
cpp调用c
1
2
3
4
5
6
7
8
9
10
11
12
|
//xx.h
extern int add(...)
//xx.c
int add(){
}
//xx.cpp
extern "C" {
#include "xx.h"
}
|
c调用cpp
1
2
3
4
5
6
7
8
9
10
|
//xx.h
extern "C"{
int add();
}
//xx.cpp
int add(){
}
//xx.c
extern int add();
|
以下一段代码展示了如何协调c和cpp的兼容性
1
2
3
4
5
6
7
8
|
#ifdef __cplusplus
extern "C"
{
#endif
int add(int x, int y);
#ifdef __cplusplus
}
#endif
|
__cplusplus 其实是一个长整数,其值与cpp标准有关
enum class的新用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <iostream>
enum class Color3 : char; // 前向声明
// 定义
enum class Color3 : char
{
RED = 'r',
BLUE = 'b',
YELLOW = 'y'
};
int main()
{
char c = static_cast<char>(Color3::RED);
std::cout<<c<<std::endl;
}
|
constexpr
constexpr
说明符声明可以在编译时求得函数或变量的值。然后这些变量和函数(若给定了合适的函数实参)即可用于仅允许编译时常量表达式之处。
const
和constexpr
具体有什么区别?见以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <iostream>
#include <array>
using namespace std;
void dis_1(const int x)
{
//错误,x是只读的变量
array<int, x> myarr{1, 2, 3, 4, 5};
cout << myarr[1] << endl;
}
void dis_2()
{
const int x = 5;
array<int, x> myarr{1, 2, 3, 4, 5};
cout << myarr[1] << endl;
}
int main()
{
dis_1(5);
dis_2();
}
|
原因在于,在dis_1()
中,const int x
只强调x是一个只可读的量(在该段函数内部不会被改变),在编译阶段x的值无法确定。严格地说,这里的x不能称之为常量。
C++ 11标准中,为了解决 const 关键字的双重语义问题,保留了 const 表示“只读”的语义,而将“常量”的语义划分给了新添加的 constexpr 关键字。因此 C++11 标准中,建议将 const 和 constexpr 的功能区分开,即凡是表达“只读”语义的场景都使用 const,表达“常量”语义的场景都使用 constexpr。使用constexpr时,编译器会对代码做对应的优化。
以下一段代码说明了两者的区别:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <iostream>
#include <array>
using namespace std;
constexpr int sqr1(int arg)
{
return arg * arg;
}
const int sqr2(int arg)
{
return arg * arg;
}
int main()
{
array<int, sqr1(10)> mylist1; //可以,因为sqr1时constexpr函数
array<int, sqr2(10)> mylist1; //不可以,因为sqr2不是constexpr函数
return 0;
}
|
C++ 14起,放松了对constexpr修饰函数的限制。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include <iostream>
constexpr int fibonacci(const int n)
{
if (n == 1)
return 1;
if (n == 2)
return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
int main()
{
std::cout << fibonacci(40) << std::endl;
}
|
在C++ 17中,if语句也可以用constexpr修饰。可以根据模板参数的值编译程序。
1
2
3
4
5
6
7
8
9
|
#include <iostream>
int main()
{
const int a = 1;
if constexpr (a)
{
}
}
|
智能指针
shared_ptr
此指针能够记录多少个shared_ptr共同指向一个对象,若为0则自动调用delete。
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 <memory>
void foo(std::shared_ptr<int> i)
{
(*i)++;
}
int main()
{
// Constructed a std::shared_ptr
std::shared_ptr<int> pointer = std::make_shared<int>(10);
// 引用计数+1
auto pointer2 = pointer;
// 查看引用计数
std::cout << pointer.use_count()<< std::endl;
// 获取原指针
int *p = pointer.get();
// 解除引用
pointer2.reset();
foo(pointer);
std::cout << *pointer << std::endl; // 11
// The shared_ptr will be destructed before leaving the scope
return 0;
}
|
unique_ptr
unique_ptr能保证只有一个指针指向某对象。
1
2
|
std::unique_ptr<int> pointer = std::make_unique<int>(10); // make_unique 从C++14 引入
std::unique_ptr<int> pointer2 = pointer; // 非法
|
weak_ptr
为解决循环引用导致的智能指针内存泄露,引入weak_ptr。
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 <memory>
using namespace std;
class B;
class A
{
public:
A() { cout << "A's constructor ..." << endl; }
~A() { cout << "A's destructor ..." << endl; }
std::weak_ptr<B> weak_b;
};
class B
{
public:
B() { cout << "B's constructor ..." << endl; }
~B() { cout << "B's destructor ..." << endl; }
std::weak_ptr<A> weak_a;
};
int main()
{
std::shared_ptr<A> aa = make_shared<A>(); //aa->object A aa计数器 1
std::shared_ptr<B> bb = make_shared<B>(); //bb->object B bb计数器 1
aa->weak_b = bb; //计数器还是1哦
bb->weak_a = aa; //计数器还是1哦
return 0;
}
|
正则表达式
使用正则表达式匹配字符串。
1
2
3
4
5
6
7
8
9
10
11
|
#include <iostream>
#include <string>
#include <regex>
int main()
{
std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"};
// 在C++ 中\ 会被作为字符串内的转义符,为使\. 作为正则表达式传递进去生效,需要对\ 进行二次转义
std::regex txt_regex("[a-z]+\\.txt");
for (const auto &fname : fnames)
std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl;
}
|
使用正则表达式搜索字串。
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 <string>
#include <regex>
int main()
{
std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"};
// 在C++ 中\ 会被作为字符串内的转义符,为使\. 作为正则表达式传递进去生效,需要对\ 进行二次转义
std::regex base_regex("([a-z]+)\\.txt");
std::smatch base_match;
for (const auto &fname : fnames)
{
if (std::regex_match(fname, base_match, base_regex))
{
// std::smatch 的第一个元素匹配整个字符串
// std::smatch 的第二个元素匹配了第一个括号表达式
// 以此类推,此处匹配的逻辑与python一样
if (base_match.size() == 2)
{
std::string base = base_match[1].str();
std::cout << "sub-match[0]: " << base_match[0].str() << std::endl;
std::cout << fname << " sub-match[1]: " << base << std::endl;
}
}
}
}
|