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;
            }
        }
    }
}
 |