C/C++开发指南

目录

一、基本操作

字符数组

char buf[recv_buff_size] = {0};
len = socket->recv(buf, recv_buff_size);
memset(buf, 0, recv_buff_size);

字符串char*

定义

char* m_content = new char[m_content_len];
memset(m_content, 0, m_content_len);
// 拷贝
memcpy(m_content+m_content_recv_len, buf_start, length);
// 也可以用
strcpy(m_content,"he11o mike!!");

// 释放
delete[] m_content;

转string

const char* s = "你好";
string str = string(s);

查找

const char CRLF[] = "\r\n";
const char* buf = "POST /reply?test=1 HTTP/1.1\r\nContent-Type: application/json\r\nPostman-Token: 2aa2d15d-cc43-4664-8d4c-8a653aa75299\r\nConnection: keep-alive\r\nContent-Length: 22\r\n";
int index = 0;
do{
    const char* buf_start = buf+index;
    const char* lineEnd = search(buf_start, buf+len, CRLF, CRLF + 2);
    int index_pre = index;
    index = index_pre+int(lineEnd-buf_start);
    std::string line(buf_start, lineEnd);
    LOG_DEBUG("m_state:%d,m_content_recv_len: %d, len:%d,index_pre:%d index: %d, line: len:%d, %s",m_state,m_content_recv_len,len,index_pre,index,line.length(), line.c_str());
    index = index +2;
}while(index<len);

结果是

POST /reply?test=1 HTTP/1.1
Content-Type: application/json
Postman-Token: 2aa2d15d-cc43-4664-8d4c-8a653aa75299
Connection: keep-alive
Content-Length: 22

比较strncmp

C 库函数 int strncmp(const char *str1, const char *str2, size_t n) 把 str1 和 str2 进行比较,最多比较前 n 个字节。
参数
str1 – 要进行比较的第一个字符串。
str2 – 要进行比较的第二个字符串。
n – 要比较的最大字符数。
返回值
该函数返回值如下:

如果返回值 < 0,则表示 str1 小于 str2。
如果返回值 > 0,则表示 str1 大于 str2。
如果返回值 = 0,则表示 str1 等于 str2。
参考:https://www.runoob.com/cprogramming/c-function-strncmp.html

比较strcasecmp忽略大小写

strcasecmp(const char* str1, const char* str2);

传递char*作为参数赋值

定义

void ApiManager::response_json(char** body, int code ,string& msg)
{

    cJSON* root = cJSON_CreateObject();
    cJSON_AddNumberToObject(root, "code", code);
    cJSON_AddStringToObject(root, "msg", msg.data());
    char *data = cJSON_Print(root);
     // 设置返回值
    char *temp = (char *)malloc(strlen(data)+1); 
	strcpy(temp,data);  //间接赋值
	*body = temp;  
    cJSON_Delete(root);
}
char* body;
string r = "ok";
response_json(body, 0, r);

调用

字符串string

方法

查找子串find

find() 函数用于在 string 字符串中查找子字符串出现的位置,如果没有查找到子字符串,那么会返回一个无穷大值 4294967295。它其中的两种原型为:

size_t find (const string& str, size_t pos = 0) const;
size_t find (const char* s, size_t pos = 0) const;

示例:

#include <iostream>
#include <string>
using namespace std;
int main(){
    string s1 = "first second third";
    string s2 = "second";
    int index = s1.find(s2,5);
    if(index < s1.length())
        cout<<"Found at index : "<< index <<endl;
    else
        cout<<"Not found"<<endl;
    return 0;
}

注意如果是判断请用int转换,否则无法进入判断

if(int(line.find(m_boundary))>-1){

}
rfind() 函数

rfind() 和 find() 很类似,同样是在字符串中查找子字符串,不同的是 find() 函数从第二个参数开始往后查找,而 rfind() 函数则最多查找到第二个参数处,如果到了第二个参数所指定的下标还没有找到子字符串,则返回一个无穷大值4294967295。

请看下面的例子:

#include

#include <string>
using namespace std;
int main(){
    string s1 = "first second third";
    string s2 = "second";
    int index = s1.rfind(s2,6);
    if(index < s1.length())
        cout<<"Found at index : "<< index <<endl;
    else
        cout<<"Not found"<<endl;
    return 0;
}

运行结果:

Found at index : 6

find_first_of() 函数

find_first_of() 函数用于查找子字符串和字符串共同具有的字符在字符串中首次出现的位置。请看下面的代码:

#include <iostream>
#include <string>
using namespace std;
int main(){
    string s1 = "first second second third";
    string s2 = "asecond";
    int index = s1.find_first_of(s2);
    if(index < s1.length())
        cout<<"Found at index : "<< index <<endl;
    else
        cout<<"Not found"<<endl;
    return 0;
}

运行结果:

Found at index : 3

本例中 s1 和 s2 共同具有的字符是 ’s’,该字符在 s1 中首次出现的下标是3,故查找结果返回3。

截取字符串substr

头文件:

#include <string> //注意没有.h
#include <iostream>
#include <string>
using namespace std ;
void main()
{
    string s="ABCD";
    cout << s.substr(2) <<endl ; //从字符串下标为2的地方开始截取,截取到末尾,输出CD
    cout << s.substr(0,2) <<endl ; //从字符串下标为0的地方开始截取,截取长度为2,输出AB
    cout << s.substr(1,2) <<endl ; //输出BC
}

int 转字符串

int i = 0;
to_string(i);

long 转字符串

unsigned long i = 0;
to_string(i);

函数返回值值string

参考:https://wenku.baidu.com/view/bdbd512b874769eae009581b6bd97f192279bfc7.html

#include<iostream>
#include<string>
using namespace std;
string TestStringC_STR()
{
    string strTest = "This is a test.";
	return strTest;
}
int main()
{
    string str1 = TestStringC_STR();
	const char *pc = str1.c_str();
    cout << pc << std::endl;
	return0;
}
    

转换char*

string cpu = "test";
char *temp = (char *)malloc(cpu.length()+ 1 ); 
strcpy(temp,id);  //间接赋值
*cpu_uid = temp;

注意:一定要用一个string变量先接收函数返回值,不然因为strTest是局部变量,会立马为空

int

字符串转int

const char* v = "80";
atoi(v);

long

unsigned long计算

double occupy = (double)a/(double)b;

结构体

定义

typedef struct NALU {
    int naluType;
    int naluLen;
    unsigned char *pData;
} T_NALU, *PT_NALU;

赋值memset

T_NALU nalu;
// memset(&nalu, 0, sizeof(T_NALU));
// 建议如下
T_NALU nalu = {};
nalu->naluLen = pos-startCodeLen;
nalu->pData = realloc(pNalu->pData, nalu->naluLen);
// char* 赋值
memcpy(nalu->pData, gBuf+startCodeLen, nalu->naluLen);

指针方式赋值calloc

nalu = (T_NALU *)calloc(1, sizeof(T_NALU));
client = malloc(sizeof(T_NALU));

文件操作

获取文件信息stat

stat():用于获取文件的状态信息,使用时需要包含**<sys/stat.h>**头文件。

int main(int argc, char* argv[])
{
    struct stat buf;
    char* path = "E:\\1.txt";
 
    int res = stat(path, &buf);
    if (res <0 )
    {
        perror("Problem getting information");
        switch (errno)
        {
        case ENOENT:
            printf("File %s not found.\n", path);
            break;
        case EINVAL:
            printf("Invalid parameter to _stat.\n");
            break;
        default:
            /* Should never be reached. */
            printf("Unexpected error in _stat.\n");
        }
    }
 
    //获取文件类型
    if (buf.st_mode & S_IFREG) // 或者用S_ISREG(buf.st_mode)
        printf("regular file\n");
    else if (buf.st_mode & S_IFDIR) // 或者用S_ISDIR(buf.st_mode)
        printf("dir \n");
    else
        printf("other\n");
 
    //获取大小
    printf("SIZE %d\n", buf.st_size);
 
    char timeBuf[] = {  };
    printf("%lld\n", buf.st_ctime);
    //文件最后访问时间
    errno_t err = ctime_s(timeBuf, ,&buf.st_atime);
    if (err)
    {
        printf("Invalid arguments to ctime_s.");
        return -;
    }
    printf("Time visit %s\n", timeBuf);
 
    //文件最后修改时间
    err = ctime_s(timeBuf, , &buf.st_mtime);
    if(err)
    {
        printf("Invalid arguments to ctime_s.");
        return -;
    }
    printf("Time modified %s\n", timeBuf);
 
    system("pause");
    return ;
}

写文件

FILE* fp = fopen(CONFIG_GB28181.data(),"w");
            fwrite(data,sizeof(char),strlen(data)+1,fp);
            fclose(fp);
            sync();
            
const char * buf, int len;
// 第一个参数要写入的内容,第二个参数写入的买个元素多大,第三个参数写入多少个元素
fwrite(buf,1,write_len,fp);

获取切换工作目录

char pwd_path[256] = "";
char *path = getenv("PWD");
strcpy(pwd_path, path);
// 合并字符串
strcat(pwd_path, "/web-http");

// 切换工作目录
chdir(pwd_path);

指针

指针转换

PyramidImageFramePtr rgb_image = nullptr;
   
for (const auto &output : xstream_out->datas_)
{
    // LOGD << output->name_ << ", type is " << output->type_;
    if (output->name_ == "rgb_image" || output->name_ == "image")
    {
        rgb_image = std::dynamic_pointer_cast<PyramidImageFrame>(output);
    }
}

智能指针

C++类循环依赖破解:前向声明与智能指针的妙用

参考:C++类循环依赖破解:前向声明与智能指针的妙用

// 文件 A.h
#pragma once

#include <memory>

class B;  // 前向声明 B 类

class A {
public:
    A();
    void SetB(std::shared_ptr<B> b);  // 使用 B 类的智能指针
    void DoSomething();
private:
    std::shared_ptr<B> b_;  // 成员变量使用 B 类的智能指针
};


// 文件 B.h
#pragma once

#include <memory>

class A;  // 前向声明 A 类

class B {
public:
    B();
    void SetA(std::shared_ptr<A> a);  // 使用 A 类的智能指针
    void DoSomething();
private:
    std::shared_ptr<A> a_;  // 成员变量使用 A 类的智能指针
};

// 文件 A.cpp
#include "A.h"
#include "B.h"

A::A() {}

void A::SetB(std::shared_ptr<B> b) {
    b_ = b;
}

void A::DoSomething() {
    if (b_) {
        b_->DoSomething();
    }
}

// 文件 B.cpp
#include "B.h"
#include "A.h"

B::B() {}

void B::SetA(std::shared_ptr<A> a) {
    a_ = a;
}

void B::DoSomething() {
    if (a_) {
        a_->DoSomething();
    }
}

在这个示例中,使用了 std::shared_ptr 作为成员变量,这是一种现代 C++ 中更安全、便利的智能指针。智能指针能够在对象不再需要时自动释放资源,避免了内存泄漏的风险。

通过这两个示例,展示了使用前向声明和指针(或智能指针)的方法来解决类之间循环依赖的问题。这种技术在大型项目中尤其有用,确保代码结构清晰而没有不必要的编译错误。

正则

解析header request_line

bool Request::parse_request_line(const string& line) {
    regex patten("^([^ ]*) ([^ ]*) HTTP/([^ ]*)$");
    smatch subMatch;
    if(regex_match(line, subMatch, patten)) {   
        m_method = subMatch[1];
        m_uri = subMatch[2];
        int index = m_uri.find('?');
        if(index>-1){
            m_path = m_uri.substr(0,index);
        }else{
            m_path = m_uri;
        }
        m_proto = subMatch[3];
        m_state = HEADERS;
        return true;
    }
    LOG_ERROR("RequestLine Error");
    return false;
}

解析headerbody


void Request::parse_header(const string& line) {
    // LOG_ERROR("ParseHeader_:%s", line.c_str());
    regex patten("^([^:]*): ?(.*)$");
    smatch subMatch;
    if(regex_match(line, subMatch, patten)) {
        m_headers[subMatch[1]] = subMatch[2];
        string key(subMatch[1]),value(subMatch[2]);
        
        //printf("key:%s\n, value: %s",key.c_str(), value.data());
        if(key == "Content-Length" ){
            m_content_len = atoi(value.c_str());
            //printf("body总长度是:%d\n",content_total_len);
        }else if(key == "Content-Type" ){
            int index = value.find("application/json");
            if(index>-1){
                m_content_type = JSON;
            }else{
                m_content_type = FORM_DATA;
                regex patten1("multipart/form-data; boundary=?(.*)$");
                smatch subMatch1;
                if(regex_match(value, subMatch1, patten1)) {
                    string boundary(subMatch1[1]);
                    m_boundary = "--"+boundary;
                    m_boundary_end = "--"+boundary+"--";
                    //printf("boundary:%s\n",boundary.c_str());
                }
            }
            //printf("body总长度是:%d\n",content_total_len);
            
        }
    }else {
        m_state = BODY;
    }
}

日期计算

引入头文件

#include <iostream>
#include <time.h>

获取当前日期时间


time_t curTime = time(NULL);
struct tm *time;
time = localtime(&curTime);
char save_name[100] = "";
sprintf(save_name,"../result/%d%02d%02d%02d%02d%02d.jpg", (1900 + time->tm_year), (1 + time->tm_mon), time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec); 

// 日期
time_t tm_time = time(0);
tm current_time = *localtime(&tm_time);
char time_str[100] = {0};
sprintf(time_str,"%d-%02d-%02d %02d:%02d:%02d", (1900 + gt->tm_year), (1 + gt->tm_mon), gt->tm_mday, gt->tm_hour, gt->tm_min, gt->tm_sec); 

// 时间
time_t tm_time = time(0);
tm current_time = *localtime(&tm_time);
char time_str[100] = {0};
sprintf(save_name,"%d-%02d-%02d", (1900 + gt->tm_year), (1 + gt->tm_mon), gt->tm_mday);    

获取当前日期时间时分秒毫秒

#include <iostream>
#include <chrono>
#include <ctime>

int main() {
    auto now = std::chrono::system_clock::now();
    auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
    auto value = now_ms.time_since_epoch().count();

    std::time_t current_time = std::chrono::system_clock::to_time_t(now);
    char buffer[100];
    std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", std::localtime(&current_time));

    std::cout << buffer << "." << (value % 1000) << std::endl;

    return 0;
}

比如计算3个月前的日期,计算60天后的日期,计算两年前的日期

https://blog.csdn.net/luchengtao11/article/details/107079786

vector

引入


#include <vector>

定义

vector<double> data;
vector<string> algoWarnImg;
vector<cv::Point> polygon[8];
int size = m_cpuOccupy.size();

插入

data.push_back(v);

获取大小v.size()

移除第一个元素

 vector<double>::iterator k = m_cpuOccupy.begin();
 m_cpuOccupy.erase(k);//删除第一个元素

map

引入

#include <map>

定义

typedef struct  {
  PT_FlvContext flvCtx;
  int first_send_idr = 0; // 是否发送了第一帧
  int chnNum; // 通道
} T_CLIENT,*PT_CLIENT;

map<int, PT_CLIENT> client_map;
map<int,PT_CLIENT>::iterator client_map_it;

插入

T_CLIENT client;
        memset(&client, 0, sizeof(T_CLIENT));
        client.first_send_idr = 0;
        client.chnNum = acpt_soc;
        client.flvCtx = &flvCtx;
 client_map.insert(pair<int, PT_CLIENT>(acpt_soc, &client));

大小client_map.size()

遍历

for(client_map_it=client_map.begin();client_map_it!=client_map.end();++client_map_it)
{
    // key
    int client_socket = client_map_it->first;
    // value
    PT_CLIENT client = client_map_it->second;
}

查找find和移除erase

client_map_it = client_map.find(client_socket);
if (client_map_it != client_map.end())
{
    // 从map中移除
    client_map.erase(client_map_it);

    close(client_socket);
}

遍历删除

for(auto i=map.begin();i!=map.end())
{
    if(i.first==value)
        i=map.erase(i);
    else
        i++;
}

for(auto i=map.begin();i!=map.end())
{
    i=map.erase(i);
}

打印

打印字符串

char* str = "112";
printf("%s",str);

打印数字

int d = 1;
printf("%d",d);

打印16进制

for(int i = 0;i<sps_nalu.naluLen;i++)
{
     printf("%2x ",sps_nalu.pData[i]);
}

函数

回调函数

目前主要用于epoll反应堆中,可以参考相关文档

void senddata(int fd,int events,void *arg);

/*描述就绪文件描述符的相关信息*/
struct myevent_s
{
    int fd;             //要监听的文件描述符
    int events;         //对应的监听事件,EPOLLIN和EPLLOUT
    void *arg;          //指向自己结构体指针
    void (*call_back)(int fd,int events,void *arg); //回调函数
    int status;         //是否在监听:1->在红黑树上(监听), 0->不在(不监听)
    char buf[BUF_SIZE];   
    int len;
    long last_active;   //记录每次加入红黑树 g_efd 的时间值
};

// 设置回调
void eventset(struct myevent_s *ev, int fd, void (*call_back)(int fd,int events,void *arg), void *arg)
{
    ev->fd = fd;
    ev->call_back = call_back;
    ev->events = 0;
    ev->arg = arg;
    ev->status = 0;
    if(ev->len <= 0)
    {
        memset(ev->buf, 0, sizeof(ev->buf));
        ev->len = 0;
    }
    ev->last_active = time(NULL); //调用eventset函数的时间
    return;
}

回调函数精解

参考:C++回调函数精解:基础使用和高级技巧一网打尽

#include <iostream>
#include <functional>

// 定义高级回调函数类型
using AdvancedCallback = std::function<int(int)>;

// 高级回调函数
int advancedCallbackFunction(int result) {
    std::cout << "高级回调函数被调用,参数为: " << result << std::endl;
    return result * 2;
}

// 函数接受高级回调函数作为参数
int performAdvancedOperation(int data, AdvancedCallback callback) {
    std::cout << "执行高级操作,参数为: " << data << std::endl;
    
    // 调用高级回调函数并获取结果
    int result = callback(data);

    std::cout << "高级操作的结果为: " << result << std::endl;
    return result;
}

int main() {
    // 使用高级回调函数作为参数调用函数
    int result = performAdvancedOperation(42, advancedCallbackFunction);
    printf("result:%d\n",result);
    // 使用Lambda表达式作为高级回调函数
    result = performAdvancedOperation(99, [](int result) {
        std::cout << "Lambda高级回调函数被调用,参数为: " << result << std::endl;
        return result * 3;
    });
    printf("result:%d\n",result);

    return 0;
}

在这个例子中,基础使用演示了如何定义和使用一个简单的回调函数。而高级使用展示了如何定义返回值非 void 的回调函数,并且使用Lambda表达式作为一种更灵活的回调方式。在高级例子中,回调函数不仅被调用,还返回了一个结果,进一步扩展了回调的灵活性。
运行结果如下:

执行高级操作,参数为: 42
高级回调函数被调用,参数为: 42
高级操作的结果为: 84
result:84
执行高级操作,参数为: 99
Lambda高级回调函数被调用,参数为: 99
高级操作的结果为: 297
result:297

虚函数

所谓“纯虚函数”,其实就是没有具体实现的虚函数,通常定义在基类中提供类似于“接口”的功能。因为没有具体实现,也即该函数没有具体功能,拥有纯虚函数的基类通常被称作“抽象类”,所以抽象类无法实例化对象,一般只能作为基类被其他派生类继承使用。继承了抽象基类的派生类必须重写所有的纯虚函数,也即为类提供具体的功能,才能实例化对象使用。
参考:C++语言中的“虚函数”就像C语言中的指针,必须要弄懂的

进制转换

字符转数字

'8'  => '8'-'0' =>8
e => 'e' - 'a' + 10 => 14
# "e8" =>0xe8
'e8' = ( 'e' - 'a' + 10)*16 + ('8'-'0') == 0xe8

内部链接和外部链接(全局变量)

C++链接性详解:外部链接与内部链接的区别与应用实例

实例源代码:

// File1.cpp
#include <iostream>

// 声明具有外部链接的全局变量
extern int globalVariable;

int main() {
    std::cout << "File1: " << globalVariable << std::endl;
    return 0;
}
// File2.cpp
#include <iostream>

// 定义具有外部链接的全局变量
int globalVariable = 42;

int main() {
    std::cout << "File2: " << globalVariable << std::endl;
    return 0;
}

内存分配

C/C++内存管理及内存泄漏详解

C语言中动态内存管理方式:malloc/calloc/realloc/free

void Test()
{
	int* p1 = (int*)malloc(sizeof(int));
	free(p1);
	int* p2 = (int*)calloc(4, sizeof(int));
	int* p3 = (int*)realloc(p2, sizeof(int) * 10);
	free(p3);
}
  • malloc:这个函数向内存申请⼀块连续可用的空间,并返回指向这块空间的指针。参数 size 指的是申请的空间的大小
  • calloc:函数的功能是为 num 个大小为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。
  • realloc:realloc 函数可以对动态开辟内存大小进行调整,返回值为调整之后的内存起始位置。
  • free:free函数用来释放动态开辟的内存。

C++内存管理方式new/delete操作内置类型

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

void Test()
{
	// 动态申请一个int类型的空间
	int* ptr4 = new int;
	// 动态申请一个int类型的空间并初始化为10
	int* ptr5 = new int(10);
	// 动态申请10个int类型的空间
	int* ptr6 = new int[3];
	delete ptr4;
	delete ptr5;
	delete[] ptr6;
}

new返回的是该数据类型的指针
在这里插入图片描述

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用
new[]和delete[],注意:匹配起来使用。

操作对象

// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数
		A* p1 = (A*)malloc(sizeof(A));
	A* p2 = new A(1);
	free(p1);
	delete p2;
	// 内置类型是几乎是一样的
	int* p3 = (int*)malloc(sizeof(int)); // C
	int* p4 = new int;
	free(p3);
	delete p4;
	A* p5 = (A*)malloc(sizeof(A) * 10);
	A* p6 = new A[10];
	free(p5);
	delete[] p6;
	return 0;

二、socket编程

常见错误

linux c++ tcosocket 解决客户端退出,服务器自动退出问题

在recv()函数返回值<0的情况下家还是那个信号屏蔽即可

#include<signal.h>
signal(SIGPIPE, SIG_IGN);

参考:https://blog.csdn.net/zbr794866300/article/details/119374038

三、线程

休眠

sleep(1); // 秒
usleep(10); // 微妙

四、异常

// try_catch_test.cpp
struct SomeOtherStruct { };
struct AnotherError { };

struct MyError { /* ... */ };
void foo() {
	SomeOtherStruct other_struct;
	throw MyError();
	return;
}

void bar() {
	try {
		foo();
	} catch (MyError err) {
		// do something with err
	} catch (AnotherError err) {
		// do something with err
	} catch (...) {
		// do something
	}
}

int main() {
	return 0;
}

五、引入库

技巧

可以使用man命令查找系统函数依赖的库
比如查找sleep

# 3表示第三页,因为第一页和第二页可能没有,输入man sleep默认是第一页
root@sony-HP-Notebook:/usr/local/cpp_demo/fun# man 3 sleep

SLEEP(3)                                                                                         Linux Programmer's Manual                                                                                         SLEEP(3)

NAME
       sleep - sleep for a specified number of seconds

SYNOPSIS
       #include <unistd.h>

       unsigned int sleep(unsigned int seconds);

常用

#include <algorithm>    // std::search
#include <vector> // vector
#include <unistd.h> // sleep
#include <regex> //regex

redis操作库hiredis

参考 hiredis使用,封装代码示例
C++封装Redis操作函数

反射是用库

参考:C++ Boost.Reflection库(现在是Boost.PFR)的下载、安装、使用详细教程
现代 C++ 编译时 结构体字段反射
·需要在CMakeLists中设置 -std=c++14·,同时也需要增加如下

# 指定C++14标准,否则boost用不了
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14)

#include <boost/pfr.hpp>
#include <boost/type_index.hpp>

const type_info& ti = typeid(T); 
        size_t size = sizeof(T);
        std::cout << "结果:"<< ti.name() <<" has " << boost::pfr::tuple_size<T>::value
        << " fields: " << boost::pfr::io(data) << "  " <<size <<"\n";
        boost::pfr::for_each_field(data, [&](const auto& field, std::size_t index) {
            std::string name;
            // boost::pfr::detail::member_to_string(field, name);
            // constexpr auto fieldName = boost::pfr::
            // constexpr auto fieldName = boost::pfr::flat_tuple_name<ti, index>();
            // std::cout << "Field Name: " << fieldName << std::endl;
            std::cout << "Field Name: " << boost::typeindex::type_id_runtime(field) << std::endl;
            std::cout << "Field Type: " << typeid(field).name() << std::endl;
            std::cout << "Field Value: " << field << std::endl;
        });

输出如下

结果:6Client has 3 fields: {1, "你好", 0}  48
Field Name: int
Field Type: i
Field Value: 1
Field Name: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
Field Type: NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
Field Value: 你好
Field Name: float
Field Type: f
Field Value: 0

序列化和反序列化

实现结构体和xml和json转换

静态反射的开源库iguana(需要c++20)

https://github.com/qicosmos/iguana

Ubpa UDRefl(需要c++20)

https://github.com/Ubpa/UDRefl

nlohmann::json(推荐)

对c++版本没有要求,
参考:
c++中nlohmann json的基本使用教程
nlohmann/json 的用法示例
引入:https://github.com/nlohmann/json/tree/develop/single_include/nlohmann/json.hpp

网络库

libevent

linuv

Boost.Asio

boost库中优秀的网络库asio

六、工具

查看内存泄露

valgrind ./a.out 

七、 编译

-O2优化

加个-O2优化

八、设计模式

发布订阅者模式

参考:C++发布订阅者模式:实现简单消息传递系统

目 录 前 言6 第1 章 文件结构 1.1 版权和版本的声明. 1.2 头文件的结构. 1.3 定义文件的结构. 1.4 头文件的作用. 1.5 目录结构. 第2 章 程序的版式 2.1 空行. 2.2 代码行. 2.3 代码行内的空格. 2.4 对齐. 2.5 长行拆分. 2.6 修饰符的位置. 2.7 注释. 2.8 类的版式. 第3 章 命名规则 3.1 共性规则. 3.2 简单的WINDOWS 应用程序命名规则. 3.3 简单的UNIX 应用程序命名规则 第4 章 表达式和基本语句 4.1 运算符的优先级. 4.2 复合表达式. 4.3 IF 语句 4.4 循环语句的效率. 4.5 FOR 语句的循环控制变量. 4.6 SWITCH 语句. 4.7 GOTO 语句. 第5 章 常量 5.1 为什么需要常量. 5.2 CONST 与 #DEFINE 的比较. 5.3 常量定义规则. 5.4 类中的常量. 第6 章 函数设计 高质量C++/C 编程指南,v 1.0 2001 Page 4 of 101 6.1 参数的规则. 6.2 返回值的规则. 6.3 函数内部实现的规则. 6.4 其它建议. 6.5 使用断言. 6.6 引用与指针的比较. 第7 章 内存管理 7.1 内存分配方式 7.2 常见的内存错误及其对策 7.3 指针与数组的对比 7.4 指针参数是如何传递内存的? 7.5 FREE 和DELETE 把指针怎么啦? 7.6 动态内存会被自动释放吗?. 7.7 杜绝“野指针”. 7.8 有了MALLOC/FREE 为什么还要NEW/DELETE ?. 7.9 内存耗尽怎么办?. 7.10 MALLOC/FREE 的使用要点 7.11 NEW/DELETE 的使用要点. 7.12 一些心得体会 第8 章 C++函数的高级特性 8.1 函数重载的概念. 8.2 成员函数的重载、覆盖与隐藏. 8.3 参数的缺省值. 8.4 运算符重载. 8.5 函数内联. 8.6 一些心得体会. 第9 章 类的构造函数、析构函数与赋值函数 9.1 构造函数与析构函数的起源. 9.2 构造函数的初始化表. 9.3 构造和析构的次序. 9.4 示例:类STRING 的构造函数与析构函数 9.5 不要轻视拷贝构造函数与赋值函数. 9.6 示例:类STRING 的拷贝构造函数与赋值函数 9.7 偷懒的办法处理拷贝构造函数与赋值函数. 9.8 如何在派生类中实现类的基本函数. 9.9 一些心得体会. 第10 章 类的继承与组合. 高质量C++/C 编程指南,v 1.0 2001 Page 5 of 101 10.1 继承 10.2 组合 第11 章 其它编程经验. 11.1 使用CONST 提高函数的健壮性 11.2 提高程序的效率 11.3 一些有益的建议 参考文献 附录A :C++/C 代码审查表. 附录B :C++/C 试题. 附录C :C++/C 试题的答案与评分标准.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值