c++

《总之,好记性不如烂笔头!把你遗忘的都记下来吧!》

C++基础语法拾遗

引用的本质

引用可以理解为变量的别名,不能超脱变量而存在。

在底层上,引用变量由指针按照指针常量的方式实现。

(1)在内存中都是占用4个字节(32bits系统中)的存储空间,存放的都是被引用对象的地址,都必须在定义的同时进行初始化。

(2)指针常量本身(以p为例)允许寻址,即&p返回指针常量(常变量)本身的地址,被引用对象用*p表示;引用变量本身(以r为例)不允许寻址,&r返回的是被引用对象的地址,而不是变量r的地址(r的地址由编译器掌握,程序员无法直接对它进行存取),被引用对象直接用r表示。

(3)凡是使用了引用变量的代码,都可以转换成使用指针常量的对应形式的代码,只不过书写形式上要繁琐一些。反过来,由于对引用变量使用方式上的限制,使用指针常量能够实现的功能,却不一定能够用引用来实现。
例如,下面的代码是合法的:

1
2
int i=5, j=6;
int* const array[]={&i,&j};

而如下代码是非法的:

1
2
int i=5, j=6;
int& array[]={i,j};

也就是说,数组元素允许是指针常量,却不允许是引用。C++语言机制如此规定,原因是避免C++语法变得过于晦涩。假如定义一个“引用的数组”,那么array[0]=8;这条语句该如何理解?是将数组元素array[0]本身的值变成8呢,还是将array[0]所引用的对象的值变成8呢?

static标识符

image-20240628132441037

可调用对象

  • std::funciton<> 类对象

    std::function模板类

    1
    std::function<void*(void)> create_object;
  • lambda表达式

    捕获 [=, &],

    返回类型推断的规则

    编译器在推断lambda表达式的返回类型时,主要依据返回语句的类型。如果lambda表达式中存在多个返回语句或含有其他非返回语句而未显式指定返回类型,编译器会假定返回类型为 void(此时需要尾置返回类型);

    然而,当lambda表达式的返回类型可以从单一的返回语句中明确推断时,编译器能够自动确定正确的返回类型。

    lambda表达式等价为一个匿名的函数对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // lambda表达式
    int factor = 2;
    auto lambda = [factor](int x) { return x * factor; };
    int result = lambda(5); // result is 10
    // 等价的匿名对象
    class AnonymousFunctor {
    public:
    AnonymousFunctor(int factor) : factor(factor) {}

    int operator()(int x) const {
    return x * factor;
    }

    private:
    int factor;
    };

    int factor = 2;
    AnonymousFunctor lambda(factor);
    int result = lambda(5); // result is 10
  • 函数对象(仿函数)

    重载了()运算符的类

  • 函数指针

定义一个函数指针,它的名字是create_object,指向返回值为void*,参数为void的函数

1
void* (*create_object)(void);

由于使用了typedef,它定义一个指向返回值为void*,接收参数为void的函数指针类型,并将它重命名为create_object

1
typedef void* (*create_object)(void);

回调函数

回调函数是一个函数指针,通过参数传递给另一个函数。当这个参数函数在某个特定的事件或条件满足时被调用时,这个函数就是回调函数。回调函数广泛应用于事件驱动编程、异步操作和处理某些操作完成后的后续步骤。

为什么要使用回调:

解耦逻辑: 回调函数可以将代码逻辑分离,使得代码更模块化、更易读、更易维护。

异步操作: 在处理异步操作(如I/O操作、网络请求)时,回调函数可以在操作完成后执行特定的逻辑,而无需阻塞主程序

事件处理: 在事件驱动编程中,回调函数用于响应特定的事件(如按钮点击、鼠标移动)

可复用性: 回调函数可以传递不同的函数,从而实现不同的行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 简单的实现
#include <iostream>
// 定义一个回调函数类型
typedef void (*CallbackFunc)(int);
// 一个执行回调函数的函数
void performOperation(int x, CallbackFunc callback) {
std::cout << "Performing operation with value: " << x << std::endl;
// 调用回调函数
callback(x);
}
// 回调函数的定义
void myCallback(int x) {
std::cout << "Callback called with value: " << x << std::endl;
}
int main() {
// 调用performOperation并传递回调函数
performOperation(5, myCallback);
return 0;
}

智能指针

std::shared_ptr
std::unique_ptr
std::weak_ptr
std::make_shared

C++11使用make_shared的优势和劣势 - 南哥的天下 - 博客园 (cnblogs.com)

std::tuple

区分初始化和赋值

初始化创建对象并为其设置初始值的过程,在这个过程中构造函数,(包括拷贝构造函数和移动构造函数)会被调用。共有两种初始化的方式

1、直接初始化,利用()

1
Base a(b);

2、复制(拷贝)初始化,利用=

1
Base a = b;

赋值已经存在的对象赋新值的过程,在这个过程中赋值运算符,(包括拷贝赋值运算符和移动赋值运算符)会被调用。

1
2
3
4
// 拷贝赋值
a = b;
// 移动赋值
a = std::move(b);

拷贝构造产生的情况

  • 使用=定义变量的时候;
  • 将对象作为实参传递给一个非引用类型的实参;
  • 从返回类型为非引用类型的函数中返回一个对象;
  • 使用花括号列表初始化数组元素或者聚合类成员;

拷贝构造参数为 const &的原因:

  • 对于const类型的参数也能传递
  • 在没有移动构造的情况下能够使用拷贝构造

一个类的拷贝构造被删除了,那么其移动构造也不会存在

隐式类型转换

1、算数类型隐式转化:

  • 在条件中。非布尔值转化为布尔值
  • 整型提升
  • 有符号转化为无符号,但不改变内存

2、类类型隐式转换:

如果构造函数只接受一个参数或者接受一个参数其余参数都存在默认值,它实际定义了此类类型隐式类型转换机制,这种构造函数叫做:转换构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
class Rational {
public:
// 支持隐式类型转换的构造函数
Rational(int numerator = 0, int denominator = 1)
: numerator_(numerator), denominator_(denominator) {
if (denominator_ == 0) {
throw std::invalid_argument("Denominator cannot be zero");
}
}
private:
int numerator_;
int denominator_;
};
1
Rational r = 5;// 将int 5隐式转化为Rational

类型转化运算符

通过实现类型转化运算符来实现,可以实现隐式类型转化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <cmath>
class Complex {
public:
Complex(double r, double i) : real(r), imag(i) {}
// 类型转换函数:将 Complex 对象转换为 double(表示模)
operator double() const {
return std::sqrt(real * real + imag * imag);
}
private:
double real;
double imag;
};
int main() {
Complex c(3.0, 4.0); // 3 + 4i
double magnitude = c; // 隐式调用类型转换函数
std::cout << "Magnitude: " << magnitude << std::endl;
return 0;
}

operator new / delete

explicit

通过将构造函数声明为explicit加以阻止隐式转化,关键字explicit只对一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转换(参数存在默认值的情况除外),也无需将它们声明为explicit的。

1
2
// 不支持隐式类型转换的构造函数
explicit Rational(int numerator = 0, int denominator = 1);

再次尝试进行隐式转化,可以看到被禁止了。

image-20240620000907126

发生隐式转化的情况是我们使用了拷贝初始化,使用explicit禁止后靠被初始化就失效了

1
2
Rational r1(5);// 直接初始化,正确
Rational r2 = 5; // 使用拷贝初始化,但explicit禁止,错误!

auto自动类型推导

引用 auto &,捕获指针 auto *

inline

内联函数的定义和实现必须在同一个文件下,也就是说inline函数定义放在头文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// MyClass.h------错误的内联实现
class MyClass {
public:
MyClass();
~MyClass();
};
// MyClass.cpp
inline MyClass::MyClass() {
// Constructor code
}
inline MyClass::~MyClass() {
// Destructor code
}
// AnotherFile.cpp
#include "MyClass.h"
void func() {
MyClass obj; // Error: undefined reference to `MyClass::MyClass()`
}

类的继承

重载、隐藏、重写(覆盖)

1、重载:从overload翻译而来,代表具有不同参数列表的同名函数,根据参数列表选择调用哪个函数,不关心函数返回类型。

1
2
3
4
5
6
int test();
int test(int a); // 参数个数
int test(int a,double b); // 参数类型
int test(double a,int a);
int test(string s) const; // const重载
...

2、隐藏:对于存在继承关系,且派生类重新定义了基类的方法就会发生隐藏。

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;
class Basic
{
public:
void Show(){
cout << "Basic:Show(void)\n";
}
virtual void Show(int a){
cout << "Basic:Show(int):" << a << endl;
}
};
class Derive : public Basic
{
public:
// 重写了基类的show()
void Show(){
cout << "Derive:Show(void)\n";
}
// 隐藏了基类的Show(int a)
};
int main(void)
{
Derive d;
d.Show();
// d.Show(3); // 错误的调用,已经被隐藏
return 0;
}

3、重写:从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
33
34
35
#include <iostream>
using namespace std;
class Basic
{
public:
void Show()
{
cout << "Basic:Show(void)\n";
}
virtual void Show(double b, int a)
{
cout << "Basic:virtual Show(double,int):" << b << "," << a << endl;
}
};
class Derive : public Basic
{
public:
void Show()
{
cout << "Derive:Show(void)\n";
}
virtual void Show(double b, int a)
{
cout << "Derive:virtual Show(double,int):" << b << "," << a << endl;
}
};
int main(void)
{
//父类指正指向子类对象
Basic *p = new Derive;
//派生类重新定义的基类方法(基类和派生类都有的)
p->Show();
p->Show(3.4, 7);
return 0;
}

早绑定,晚绑定

虚函数表

final禁止继承

左值、右值、亡值、表达式

引用折叠

引用折叠只能应用于间接创建的引用的引用,如类型别名或模板参数

  • X& &、X& &&、和X&& &都折叠成类型X&
  • X&& && 折叠成类型X&&

std::move

显式的将一个左值转化为对应的右值引用类型。

1
2
int &&rr1 = 42;
int &&rr3 = std::move(rr1);

定义

1
2
3
4
template<typename T>
typename std::remove_reference<T>::type&& my_move(T &&t){
return static_cast<typename std::remove_reference<T>::type &&>(t);
}

std::ref

std::bind

为函数绑定参数

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
#include<iostream>
#include<functional>
#include<vector>
#include<algorithm>
// args参数列表 _1,_2 定义在命名空间std::placeholders下可以使用using简化
/*绑定参数args的四种情况:
第1种情况:引用包装; 第2种情况:绑定表达式; 第3种情况:占位符; 第4种情况:通常参数*/
using namespace std::placeholders;
bool check_size(const std::string &s, std::string::size_type sz){
return s.size() < sz;
}

int sum(int a,int b){
return a;
}
// for_each()打印的函数替换版本
std::ostream &print(std::ostream &os,const std::string&str,char c){
return os << str << c << ' ';
}
void shorter_words(std::vector<std::string> &vec_str){
auto it = std::stable_partition(vec_str.begin(), vec_str.end(), std::bind(check_size,_1,6));
std::for_each(vec_str.cbegin(), vec_str.cend(), [](const std::string &str)
{ std::cout << str << ' '; });
puts("");
auto size = it - vec_str.cbegin();
std::cout <<"words less than 6 count is:"<< size;
}
int main(int argc, char const *argv[])
{
// 绑定参数的check_size函数
auto check_6 = std::bind(check_size, _1, 6); // 对应函数 bool check_6(_1);
std::string a = "hello12";
std::cout << check_6(a) << std::endl;
// 可以用bind掉转参数
auto u_sum = std::bind(sum, _2, _1);
std::cout << u_sum(1, 2)<<std::endl;
// 绑定引用参数 ref(),cref()
std::vector<std::string> words = {"haha", "funcking", "bind", "is","so", "learning!"};
std::ostream os(std::cout.rdbuf());
char c = 'a';
std::for_each(words.begin(), words.end(), [&os, c](const std::string &str)
{ os << str<< c<<' '; });
puts("");
auto print_bind = std::bind(print, ref(os), _1, c);
std::for_each(words.begin(), words.end(), print_bind);
puts("");
// 单词计数
shorter_words(words);
return 0;
}

绑定类的成员函数必须传递类对象或者类对象指针

1
2
3
4
BindTestClass bindTestClass(33, "zack");
// 绑定类的成员函数,一定要传递对象给bind的第二个参数,可以是类对象,也可以是类对象的指针
// 如果要修改类成员,必须传递类对象的指针
auto memberbind = bind(&BindTestClass::MemberFun, &bindTestClass, placeholders::_1, placeholders::_2);

模板编程

std::forward完美转发

通常情况下,使用forward传递定位为模板类型的右值引用的函数参数。通过返回类型上的引用折叠,forward可以保持给定实参的左值/右值属性。

1
2
3
4
5
template <typename Type>
intermediary(Type &&arg){
finalFcn(std::forward<Type>(arg));
...
}

源码:

1
2
3
4
template<typename _Tp>_GLIBCXX_NODISCARD
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); }

可变参数模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<typename T>
std::ostream &print(std::ostream &os, const T &t){
return os << t;
}
template<typename T, typename... Args>
std::ostream &print(std::ostream &os, const T &t, const Args&... rest){
os << t << " ";
return print(os, rest...);
}
int main(int argc, char const *argv[])
{
print(std::cout, "hello", 42," good ");
return 0;
}

sizeof…运算符

1
2
// 当需要知道包中的参数个数时,使用sizeof...
cout<< sizeof...(args)<<endl;

折叠表达式优化C++17

1
2
3
4
5
6
template<typename... Args>
std::ostream& print2(std::ostream &os, const Args&... args){
// 使用折叠表达式对每一个参数进行操作
((os << args << " "), ...);
return os;
}

模板特化

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
#include<iostream>
#include<string>
#include<string.h>
// 函数模板的特化
template<typename T1,typename T2>
int compare(const T1 &v1, const T2& v2){
if(v1<v2)
return -1;
if(v2<v2)
return 1;
return 0;
}
template <>
int compare(const std::string &v1, const std::string &v2){
std::cout << "特化版本" << std::endl;
return strcmp(v1.c_str(), v2.c_str());
}
// 类模板的特化
template <class T1, class T2>
class Data
{
public:
Data() { cout << "Data<T1, T2>" << endl; }

private:
T1 _d1;
T2 _d2;
};

//全特化<int,char>
template <>
class Data<int, char>
{
public:
Data() { std::cout << "Data<int, char>" << std::endl; }

private:
int _d1;
char _d2;
};

void TestVector()
{
Data<int, int> d1;
Data<int, char> d2;
}

int main(int argc, char const *argv[])
{
std::cout << compare(std::string("he"), std::string("he"));
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<exception> //属于标准命名空间

class myExpection: public std::exception
{
public:
const char * what() const throw(){
return "C++ expection";
}

};
class TimerExpection: public std::exception{
public:
TimerExpection() = default;
TimerExpection(unsigned code, const std::string &msg) : m_code(code), m_errorMsg(msg){}
const char* what() const noexcept override{
const std::string msg = "error code : ";
return msg.c_str();
}
private:
unsigned m_code;
std::string m_errorMsg;
};
int main(int argc, char const *argv[])
{
try{
throw myExpection();
}catch(myExpection &e){
std::cout << e.what() << std::endl;
}
return 0;
}

正则表达式

随机数

C++机制

前置声明

前向声明的适用情况

  1. 指向未定义类型的指针或引用: 如果一个类仅使用另一个类的指针或引用,并不需要访问该类的成员,可以使用前向声明。这可以减少不必要的头文件包含。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Session; // 前向声明
    class LogicNode {
    public:
    std::shared_ptr<Session> getSession() const;
    // 其他成员函数和变量
    private:
    std::shared_ptr<Session> p_session; // 仅使用指针
    // 其他成员变量
    };
  2. 避免循环依赖: 当两个类互相包含对方的实例时,前向声明可以打破这种循环依赖。

    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
    cpp复制代码// A.h
    #ifndef A_H
    #define A_H

    class B; // 前向声明

    class A {
    public:
    void doSomethingWithB(B* b);
    private:
    B* bInstance; // 使用指针
    };
    #endif // A_H

    // B.h
    #ifndef B_H
    #define B_H
    #include "A.h" // 包含A.h,因为需要访问A的成员

    class B {
    public:
    void doSomethingWithA(A* a);
    private:
    A* aInstance; // 使用指针
    };

    #endif // B_H

前向声明的限制

  1. 不能定义成员变量
  2. 不能调用成员函数

RAII机制

​ RAII是Resource Acquisition Is Initialization的缩写,即“资源获取即初始化”。它是C++语言的一种管理资源、避免资源泄漏的惯用法,利用栈的特点来实现,这一概念最早由Bjarne Stroustrup提出。在函数中由栈管理的临时对象,在函数结束时会自动析构,从而自动释放资源,因此,我们可以通过构造函数获取资源,通过析构函数释放资源。即:

1
2
3
4
5
6
7
Object() {
// acquire resource in constructor
}

~Object() {
// release resource in destructor
}

RVO、NRVO编译器优化

GCC 中使用 -fno-elide-constructors可以禁用RVO、NRVO优化

RVO(未命名的局部变量返回值优化)/NRVO(命名的局部变量返回值优化): 编译器会尝试应用返回值优化。如果成功,局部变量将直接在调用者的存储空间中构造,不会调用拷贝或移动构造函数。

如果禁止了RVO/NRVO优化,返回局部变量应该优先使用移动构造函数,如果没有移动构造函数就使用拷贝构造函数。如果开启RVO/NRVO优化就不使用任何构造函数。

闭包

C++反射

反射机制允许程序在运行时借助API取得任何类的内部信息,并能直接操作对象的内部属性和方法。

C++不支持反射,PRC,WEB MVC,对象序列化 都依赖反射。

1、类对象反射

image-20240706174644802

2、类成员数据反射

3、类成员函数反射

CRTP机制

奇异递归模板方法,通过继承实例为自身的模板方法

std::chrono

其定于都是在std::chrono这个命名空间下的

1、Time Points 时间点

表示一个特定的时间点,类模板,需要传递采用的时钟信息。

1
2
// 获取当前所在的时间点/时间戳
const std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
time_since_epoch()

返回当前时间到时间纪元开始的durations

1
std::chrono::duration_cast<std::chrono::hours>(p1.time_since_epoch()).count() 

2、Durations 时间长度

表示时间的长度。

1 秒 = 1000 毫秒 = 1000 * 1000 微秒 = 100010001000 纳秒 = 100010001000*1000

类型 定义
std::chrono::nanoseconds std::chrono::duration<*/* int64 */*, std::nano>
std::chrono::microseconds std::chrono::duration<*/* int55 */*, std::micro>
std::chrono::milliseconds std::chrono::duration<*/* int45 */*, std::milli>
std::chrono::seconds std::chrono::duration<*/* int35 */*>
std::chrono::minutes std::chrono::duration<*/* int29 */*, std::ratio<60>>
std::chrono::hours std::chrono::duration<*/* int23 */*, std::ratio<3600>>
std::chrono::days (C++20 起) std::chrono::duration<*/* int25 */*, std::ratio<86400>>
std::chrono::weeks (C++20 起) std::chrono::duration<*/* int22 */*, std::ratio<604800>>
std::chrono::months (C++20 起) std::chrono::duration<*/* int20 */*, std::ratio<2629746>>
std::chrono::years (C++20 起) std::chrono::duration<*/* int17 */*, std::ratio<31556952>>
std::chrono::duration_cast<>

转化时间的单位;

1
std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count()
count()

返回时长计数次数的有符号算数类型;

3、Clocks 时钟

获取当前的时间点。

  • system_clock 表示系统的实际时间,不会受到系统时间调整的影响
  • steady_clock 稳定的时钟,时间不调整
  • high_resolution_clock 提供最小可表示的时间间隔

成员函数

now()

返回表示时间中的当前时间点Time_Point

应用实例:

定时器类

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
class Timer{
public:
Timer() = default;
void start(){
if(m_timeRuning){
throw TimerExpection(1, "计时器被重复启动");
}
m_timeRuning = true;
m_begin = std::chrono::system_clock::now();
}
void stop(){
if(!m_timeRuning){
throw TimerExpection(2, "计时器还没有启动");
}
m_timeRuning = false;
m_end = std::chrono::system_clock::now();
}
double elapsed(){
if(m_timeRuning){
return std::chrono::duration<double>(std::chrono::system_clock::now() - m_begin).count();
}else{
if (m_begin == std::chrono::time_point <std::chrono::system_clock>()){
throw TimerExpection(3, "计时器还未启动");
}else{
return std::chrono::duration<double>(m_end - m_begin).count();
}
}
}
private:
bool m_timeRuning = false;
std::chrono::time_point<std::chrono::system_clock> m_begin;
std::chrono::time_point<std::chrono::system_clock> m_end;
};

int main(){
Timer time;
time.start();
std::this_thread::sleep_for(std::chrono::seconds(3));
time.stop();
std::cout << time.elapsed() << std::endl;
return 0;
}

C风格时间日期

std::time_t

1
2
3
4
5
6
7
8
9
10
11
// 获取当前时间点
auto now = std::chrono::system_clock::now();

// 转换为time_t类型
std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);

// 将time_t类型的时间转换为tm结构
std::tm* now_tm = std::localtime(&now_time_t);

// 使用std::put_time格式化输出时间
std::cout << "Current time: " << std::put_time(now_tm, "%Y-%m-%d %H:%M:%S") << std::endl;

外部库

Boost

jsoncpp

json格式解析

json中仅支持两种数据结构

  • 对象(object):以{开头
  • 数组(array):以[开头

pair键值对:

一个pair键值对的结构通常是: string(key) : value(value),其中key一般使用字符串,也可以使用数字,建议key只使用字符串。value取值较为随意,可以是json支持的任意类型:object,array,string,number,true,false,null,这说明了可以嵌套类型。

对象object:

object是多个pair的集合,以{开始,}作为结束,不同的pair之间用英文逗号来分割。object之间的pairt数据是无序的。

1
2
3
4
{
"name" : "nick",
"age" : 18
}

数组array:

array是value的有序集合,以[开始,]作为结束,不同array元素之间也用英文逗号来分割。

1
2
3
4
5
6
7
8
9
10
11
[
{
"name":"nick"
},
{
"age":18
},
{
"domin":"cn"
}
]

从文件中解析json:

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
// color.json
{
"name" : "nick",
"age" : 18
}
// testJson.cpp
#include <iostream>
#include <fstream>
#include "jsoncpp.cpp"
void parse_from_file(){
std::ifstream ifs;
ifs.open("color.json");
Json::Reader reader;
Json::Value root;
// 从reader中反序列化
if(!reader.parse(ifs, root, false)){
std::cerr << "parse failed \n";
return;
}
std::string name = root["name"].asString();
int age = root["age"].asInt();
std::cout<<name <<" "<<age;
ifs.close();
}
int main(int argc, char const *argv[])
{
parse_from_file();
return 0;
}

protobuf

grpc

redis

启动redis

1
.\redis-server.exe .\redis.windows.conf

image-20240728084005178

字符串String

设置一个键值对:

1
SET key value

当键不存在时,设置键的值:

1
SETNX name value

获取一个值:

1
GET key

Redis默认不支持中文显示,值作为中文将显示为二进制字符串,使用–raw可以显示原始数据

设置一个键的过期时间,单位秒:

1
EXPIRE key 10

设置一个带有过期时间的键值:

1
SETEX key 10 value

查看一个键的过期时间:

1
TTL key

List列表

将元素添加到头部

1
LPUSH ListName value

从列表尾部添加元素

1
RPUSH ListName value

删除列表头部/尾部中的指定数量的元素

1
2
LPOP ListName 元素数
RPOP ListName 元素数

查看列表中的所有元素,-1代表列表中的最后一个元素位置

1
LRANGE ListName 起始位置 -1

查看列表的长度

1
LLEN ListName

删除范围之外的元素

1
LTRIM ListName 范围1 范围2

Set

不可重复的元素集合

添加元素,重复添加将返回false

1
SADD SetName value

查看集合中的内容

1
SMEMBERS SetName

判断元素是否在集合中

1
SISMEMBER SetName value

删除元素

1
SERM SetName value

同时Reids支持集合之间的运算。

SortedSet

有序集合操作

image-20240728091116901

Hash

记录键值对的集合

添加键值对

1
HSET HashName key value

获取值

1
HGET HashName key

获取所有键值对

1
HGETALL HashName

删除键值对

1
HDEL HashName key

判断键是否存在

1
HEXISTS HashName key

订阅者模式

消息队列Stream

杂谈:

linux高性能服务器,单例模式,工厂模式,木朵网络库,一个方向深挖,opengl, opencv


c++
http://example.com/2024/07/31/c++/
作者
John Doe
发布于
2024年7月31日
许可协议