C++ 高级用法选讲

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 说明符声明可以在编译时求得函数或变量的值。然后这些变量和函数(若给定了合适的函数实参)即可用于仅允许编译时常量表达式之处。

constconstexpr具体有什么区别?见以下代码:

 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;
            }
        }
    }
}
Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy
visitors: total visits: time(s) reads: time(s)