C++开发(软件开发)常见面试题

目录

1、C++里指针和数组的区别

2、C++中空指针请使用nullptr不要使用NULL

3、http/https区别和头部结构?

4、有了mac地址为什么还要ip地址?ip地址的作用

5、有了路由器为什么还要交换机?

6、面向对象三大特性

7、友元函数

8、大端小端

9、野指针

10、static

11、指针*和引用&的区别,用sizeof时有什么不同,为什么需要引用?

12、静态变量什么时候初始化?

13、new和malloc区别?原理

14、内存泄漏、如果出现怎么排查和定位?

15、const以及函数后加const?

16、操作系统里堆和栈的区别

17、基类和派生类中构造函数和析构函数的顺序

18、strcpy和memcpy的区别,谁的性能好?

19、malloc和new的区别

20、静态多态和动态多态

21、内存分区

22、文件编译过程

23、虚拟内存

24、浏览器输入网址后执行的过程?

25、进程和线程区别

26、死锁?怎么产生的?

27、设计模式?

28、右值引用

29、MVC

30、分布式项目中客户端与服务端通信流程

31、tcp相比于udp的区别

32、RAII

33、分布式系统


1、C++里指针和数组的区别

复制代码
先来看一个经典问题

char s1[]="hello";
char *s2 ="hello";

1、s1的值是放在栈上的,值是可以修改的,而hello是一个字符串常量放在静态存储区是不能修改的。

2、内存大小不一样

复制代码
#include<stdio.h>

int main(){
    char s1[]="hello";
    char *s2="hello";
    
    puts(s1);
    puts(s2);
    
    printf(""%ld %ld\n",sizeof(s1),sizeof(s2));
    return 0;
}

3、无法返回局部变量的地址,栈上的值随着函数调用结束,内存已经回收了

上面这种编译器会报警,下面则不会

2、C++中空指针请使用nullptr不要使用NULL

C++中NULL定义就是整数字面量0

对于C++函数,由于存在重载,使用NULL而不是nullptr可能导致函数走错重载。

C中定义NULL为(void* )0,确实是代表空指针。使用时隐式转换成对应的需要类型的空指针。

C++中void指针不能隐式转换成其他指针,所以无法按照C那样定义。

C++中保留NULL可以兼容一些C style的代码,对于这些库,不会使用到函数重载,不会产生对应的问题。但对于纯C++程序,请使用nullptr表示空指针。

3、http/https区别和头部结构?

HTTP:超文本传输协议,被用于在web浏览器和网站服务器之间传递消息,以明文的方式发送内容,不提供任何方式的数据加密,传输端口为80,特点是简单快捷,灵活,无状态,每次请求都是独立的,上一次请求和下一次请求互不相干。比如登录某个网站后,本来不需要再登陆,不知道上次请求已经登陆过了

HTTPS:HTTP+SSL/TLS 基于TLS/SSL协议加密进行,引入了会话保持,session和cookie,状态记录-登录验证,TCP传输,拿到的是密文,传输端口为443,SSL/TLS协议依靠证书来验证服务器的身份。

4、有了mac地址为什么还要ip地址?ip地址的作用

IP(包裹地址):一个为互联网的每一个网络和每一台主机分配的逻辑地址

Mac(收件人信息):媒体访问控制地址,局域网地址,以太网地址,物理地址,是一个用来确认网上设备位置的地址

Mac地址用于标示一个网卡,一台设备若有一个或多个网卡,则每个网卡都需要并会有一个唯一的Mac地址,表明身份

只有Mac地址可以传输数据,只要同处于一个局域网内

ISP:互联网服务提供商。

5、有了路由器为什么还要交换机?

路由器:与外部通讯

交换机:提供内网通讯

不是每个网络需要路由器,比如企业,学习,医院等,内部通信需要大量的接入设备,只需要交换机,一台交换机可以接入几十台设备,而仅需一个路由器提供对外访问,更大型的网络里,需要对内部网进行划分若干小内网,实现内部小网之间互相访问,可采用带路由功能的交换机,即三层交换机

路由器侧重点是共享,交换机功能不强,侧重点是多口,构成局域网

如果有50台computer,不得不用交换机,小路由器便宜,但代理能力更弱,只能代理几台,若加很多口,又没有能力代理,没用,但口多代理强的路由器很贵如只要交换机,加了路由,价也贵了,功能也多余的.

6、面向对象三大特性

(1)封装性:将客观事物抽象成类,每个类对自身的数据和方法实行protection

(2)继承性:广义的继承有三种实现形式:实现继承(使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。

(3)多态性:是将父类对象设置成为和一个或更多它的子对象相等的技术。用子类对象给父类对象赋值之后,父类对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。

封装是实现面向对象程序设计的第一步,将数据或函数等集合在一个个的单元(类)中。封装的意义就是保护或防止数据被无意破坏。

继承主要用来实现重用代码,节省开发时间,子类可以继承父类。

多态是指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针来调用实现派生类中的方法。

继承是面向对象编程中实现代码复用的重要手段。通过继承,子类可以继承父类的属性和方法,并可以添加或覆盖父类的方法。这使得子类能够共享父类的代码,并扩展或修改其功能。

|--------------|--------------|--------------|------------|
| | public继承 | protected继承 | private继承 |
| 基类的public | 派生出public | 派生出protected | 派生出private |
| 基类的protected | 派生出protected | 派生出protected | 派生出private |
| 基类的private | 在派生类不可见 | 在派生类不可见 | 在派生类不可见 |

private基类无论以什么方式继承到派生类中都是不可见的

使用class时默认是private,使用struct默认是public

封装是面向对象编程的核心概念之一,它通过将数据和操作数据的方法绑定到一个对象中,隐藏对象的内部状态和实现细节,只对外提供公共接口。这样可以提高代码的安全性和可维护性。

封装:将类的实现细节隐藏,暴露一个简洁。清晰的接口,提高代码的可重用性、实用性、耦合度、可维护性

访问控制权限:

一个类的public变量、函数可以通过类的实例变量访问,

protected无法通过变量访问,但可以通过类的友元函数、友元类访问

private同protected

多态:多种形态去完成某个行为,当不同对象去完成时会产生不同的状态

买票:学生半价、普通人全价

实现:重写/覆盖:子类有一个跟父类完全相同的虚函数,子类的虚函数重写了基类的虚函数

子类父类都要这个虚函数

多态是面向对象编程中实现接口统一和灵活性的关键特性。通过多态,不同的对象可以对同一消息作出不同的响应。这使程序在运行时能够根据对象的实际类型来执行相应的操作,提高了代码的灵活性和可扩展性。分为静态和动态两种

静态(编译时):重载、泛型编程、重载运算符、模板,编译时自动绑定好

动态(运行时):虚函数、virtual、回调函数

虚函数有一个虚函数表,虚函数表指针一般有四个字节,虚表指针在内存哪个分区取决于在堆上创建还是栈上

虚函数和纯虚函数,通过使用指向基类对象的指针或引用调用虚函数

多态分为两种,一种是编译时多态,比如函数重载和模板,编译器在编译代码时会根据不同的代码匹配相应的函数。另一种是运行时多态,主要通过虚函数实现。在定义类时,定义一个虚函数,当在子类中使用时,只需要重写这个虚函数。

重写虚函数:一般先写函数的返回值、函数名和形参,然后在形参后面加上override关键字。如果是纯虚函数,必须重写才能使用。如果不是纯虚函数,override关键字可以不加。

C++在运行时,虚函数通过虚表(vtable)实现。当一个对象调用虚函数时,程序会通过该对象的虚表指针找到相应的函数地址并进行调用。

虚函数重写使用override关键字重写虚函数。纯虚函数必须重写,非纯虚函数可以不加override。

7、友元函数

友元函数是 一个特性,它允许一个或多个非成员函数访问一个类的私有和保护成员。友元函数不是类的成员函数,但它可以访问类的所有成员,包括私有成员。这种机制提供了一种突破数据封装和隐藏的方式,使得非成员函数能够直接操作类的内部数据。然而,使用友元函数需要谨慎,因为它可能会破坏封装性并增加代码的复杂性。

8、大端小端

大端小端是计算机存储数据的一种字节序方式。

大端模式(Big-Endian)是指高位字节存储在内存的低地址处,而低位字节存储在内存的高地址处;

小端模式(Little-Endian)则相反,低位字节存储在内存的低地址处,高位字节存储在内存的高地址处。这两种模式在跨平台编程和网络通信中需要特别注意。

如果数据类型占用的内存空间大于1字节,CPU把数据存放在内存中的方式有两种:

(1)大端序(Big Endian):低位字节存放在高位,高位字节存放在低位。

(2)小端序(Little Endia):低位字节存放在低位,高位字节存放在高位

大小端含义

大端:低地址存放数据的高位,高地址存放数据的低位. 即CPU对操作数的存放顺序为高字节到低字节.

如存放数据0x123456:

数据: 0x56 | 0x34 | 0x12

地址: 0x01 | 0x02 | 0x03

小端:低地址存放数据的低位,高地址存放数据的高位. 即CPU对操作数的存放顺序为低字节到高字节.

如存放数据0x123456:

数据: 0x12 | 0x34 | 0x56

地址: 0x01 | 0x02 | 0x03

判断大小端

1.联合体判断:因为联合体的大小为联合体中所有数据当中类型最大那个类型的大小,则通过char和int可以判断.

复制代码
#include<iostream>
using namespace std;
union A{
char a;
int b;
/*

char 占一个字节,int占四个字节
所以联合体的大小为四个字节,且a b公用一块内存
当对b赋值为1时,则b=0x00 00 00 01;
当为大端模式时:读取a得到的值为0;
当为小端模式时:读取a得到的值为1;
*/

};

int main(){
A U;
U.b=1;
if(U.a){
cout<<"小端模式"<<endl;
}
else{
cout<<"大端模式"<<endl;
}
return 0;
}

通过对四字节的 int* 强转为一字节的 char*

复制代码
#include<iostream>
using namespace std;
int main()
{
int i = 1;
//&i为i的地址,此时为int*类型
//(char*)将int*强转为char*
//*取该char*的内容
if (*(char*)&i) {
cout<<"小端模式"<<endl;
}
else{
cout<<"大端模式"<<endl;
}
return 0;
}

9、野指针

野指针是指指向已经被释放的内存空间的指针,或者是一个未被初始化的指针。使用野指针可能会导致程序崩溃或数据损坏。为了避免野指针问题,需要在使用指针前进行初始化,并在释放内存后将指针置为nullptr。

10、static

修饰静态局部变量,会改变局部变量的存储位置,从而使得局部变量的生命周期变长,延长至程序结束才销毁,普通的局部变量创建后是放在栈区,这种局部变量进入作用域时创建,出了作用域就销毁。static修饰局部变量只改变生命周期,不改变作用域

修饰全局变量,会改变全局变量的链接属性,使全局变量无法被其他文件调用,作用域变小

全局变量:程序所有源文件,对象及函数都可以调用,生命周期贯穿整个程序,想被另一个文件使用时需要进行外部声明extern

修饰函数:改变函数链接属性,从而使作用域变小,函数本身有外部链接属性,被修饰后成了内部链接属性

11、指针*和引用&的区别,用sizeof时有什么不同,为什么需要引用?

指针:内存地址,指针变量是用来存放内存地址的变量

引用:给已存在的变量取一个别名,编译器不会给引用变量开辟内存空间,共用一块内存空间,主要作用是修饰函数的形参和返回值

在C++中,函数和返回值的传递方式有三种:值传递、指针传递、引用传递,引用具有指针的效率,有=又具有变量使用的方便性

区别:

引用给予程序元素完成其功能的最小权限,指针能无约束地操作内存中的任何东西,非常危险

引用定义必须初始化,指针没有,但尽量初始化,防止野指针

引用在初始化后不能再引用其他实体,指针可以

没有null引用,但是有nullptr指针

在sizeof中含义不同,引用结果为引用类型大小,但指针是地址空间所占字节个数(32位平台占4个字节)

有多级指针,但没有多级引用

12、静态变量什么时候初始化?

初始化只有一次,但可以多次复制,在主程序之前编译器已经为其分配好了内存

静态变量和全局变量一样,都存放在全局区域(数据区)

如果是c:int a=1;int b=a; 是错误的,在编译时才初始化

C++:int a=1;static int b=a;

由于C++引入对象后要进行初始化必须执行相应的构造函数和析构函数,经常会进行特定操作,所以C++为全局或静态对象时有首次用到时才会进行构造,C++中内置类型比如int、double、char都升级成了类,在对象第一次使用时初始化.

13、new和malloc区别?原理

new、delete是关键字,需要编译器支持,

malloc、free是库函数,需要引入相应头文件

malloc:申请空间时要填入申请内存大小,int *m=(int*)malloc(4);堆空间

new:根据类型分配内存 int *a=new int(0); 自由存储区

c++内存分为:堆、栈、自由存储区、全局/静态存储区、常量存储区

堆是操作系统维护的一块特殊内存,提供了动态分配功能,当运行程序调用malloc时会从中分配,调用free归还内存

自由存储区是c++中动态分配和释放对象的概念

通过new分配的内存区域可以称为自由存储区,自由存储区可以是堆、全局/静态存储区等

new返回对象类型的指针,类型与对象匹配

malloc内存分配成功返回void *,需要通过强制类型转换将void*转换成所需,分配失败返回null

new失败会抛出异常,

malloc可以通过realloc扩张,new没有

(1)new是C++中的操作符,malloc是C中的一个函数。

(2)new不止分配内存,而且会调用类的构造函数,同理delete调用类的析构函数,而malloc只分配内存,不会进行初始化类成员的工作,同样free也不会调用析构函数。

(3)内存泄露对于malloc或者new都可以检查出来,区别在于new可以指明是文件的哪一行,而malloc没有这些信息。

(4)new可以认为是malloc加构造函数的执行,new出来的指针是直接带类型信息的,而malloc返回的值都是void指针

14、内存泄漏、如果出现怎么排查和定位?

分配的内存没有被正确释放,导致内存被占用过多,主要与动态内存分配有关,

int *p=new int;

delete p;

对同一个指针重新分配内存:int *p=new int;

p=new int;

导致程序运行效率下降、程序出现安全漏洞、内存资源枯竭

15、const以及函数后加const?

const int a 常整型数

int const a 常整型数

const int *a 指向常整型数的指针,即a的值可以变,*a不可变

int* const a 常指针,a的值不能变,*a可变

int const *a const 指向常整型数的常指针

const修饰函数参数:只能修饰输入作用的参数,如果输入参数为指针,加上const起到保护指针意外修改的作用,

const int fun();没有必要,只是个临时值,最终这个值会复制给接受它的变量

const修饰成员函数:为了保护成员变量,要求const函数不能修改成员变量,否则编译报错

const对象只能访问const成员函数,非const对象可以访问任何成员函数,包括const成员函数

const成员函数可以分为所有成员变量,但只能访问const的成员函数

const成员函数不能修改任何成员变量,除非变量用mutable修饰

在const成员函数中成员变量都变成const属性,无法再次修改

16、操作系统里堆和栈的区别

堆:由程序员分配释放,若不释放,程序结束时可能由OS回收,堆的内部地址生长方向与栈相反,由低到高

栈:由操作系统自动分配释放,存放函数的参数值,局部变量,栈的内部地址是由高到低分配的,因此后定义的变量在栈中的地址低于先定义的变量

空间大小:栈的值是固定的,由编译器不同而不同,一般为2M,较小堆理论上可以分配虚拟内存大小的空间,堆区的内存空间是由链表组织的,是不连续的

17、基类和派生类中构造函数和析构函数的顺序

构造:先基类,再派生类

析构:先派生类,再基类

多个基类的调用跟基类继承的顺序有关

18、strcpy和memcpy的区别,谁的性能好?

strcpy和memcpy都是C语言中常用的字符串复制函数

主要区别

复制内容:

strcpy:只能复制以'\0'结尾的字符串。

memcpy:可以复制任意内容,包括字符数组、整型、结构体、类等,只要指定了正确的长度。

复制方法:

strcpy:不需要指定长度,遇到字符串结束符'\0'时停止复制。如果目标空间不够大,可能会导致缓冲区溢出。

memcpy:根据其第三个参数(即要复制的字节数)来决定复制的长度,不会自动停止。因此,需要确保目标空间足够大以容纳要复制的数据。

参数类型:

strcpy:参数是字符指针(char*)。

memcpy:参数是void指针(void*),提供了更大的灵活性,可以复制任何类型的数据。

安全性:

strcpy:由于不检查目标空间是否足够大,可能会导致缓冲区溢出,存在安全隐患。

memcpy:在正确使用时(即确保目标空间足够大),相对更安全。但如果不小心指定了错误的长度,也可能导致内存问题。

性能表现

对于较短的字符串,strcpy可能需要额外的处理来查找字符串结束符'\0',这可能会使其比memcpy稍微慢一些。然而,这种差异通常非常小,并且在大多数情况下可以忽略不计。

对于较长的字符串或需要复制非字符串数据时,memcpy的性能可能会更好,因为它不需要处理字符串结束符,并且可以直接根据指定的长度进行复制。

在实际应用中,性能的差异还受到编译器优化、内存布局、CPU缓存等多种因素的影响。因此,很难给出一个绝对的结论说哪个函数性能更好。通常,选择哪个函数应该基于具体的使用场景和需求。

总结

如果需要复制以'\0'结尾的字符串,并且目标空间足够大以容纳整个字符串(包括结束符),则可以使用strcpy。但需要注意避免缓冲区溢出的问题。

如果需要复制任意类型的数据或指定长度的字符数组,并且希望避免字符串结束符的处理,则可以使用memcpy。但需要确保目标空间足够大以容纳要复制的数据。

19、malloc和new的区别

malloc函数用于在堆上分配指定字节数的内存,并返回一个指向该内存的指针(类型为void*,通常需要强制类型转换)。

初始化:malloc分配的内存是未初始化的,即内存中的值是未定义的。

释放:使用malloc分配的内存必须使用free函数来释放,否则会导致内存泄漏。

灵活性:malloc只负责分配内存,不涉及对象的构造。因此,对于类类型的对象,仅使用malloc是不足够的,还需要手动调用构造函数。

new运算符是C++中特有的。它用于在堆上分配内存并构造对象。new运算符返回一个指向新创建对象的指针。

初始化:使用new分配并构造的对象会被自动初始化。对于内置类型,将调用默认构造函数(如果适用)或进行零初始化;对于类类型,将调用其构造函数。

释放:使用new分配并构造的对象必须使用delete运算符来释放和销毁。delete运算符会首先调用对象的析构函数,然后释放内存。

便利性:new运算符结合了内存分配和对象构造两个步骤,使代码更加简洁和易于管理。

malloc和new都用于在堆上动态分配内存,但new还负责对象的构造。

malloc返回的是void*类型的指针,需要手动进行类型转换;而new返回的是具体类型的指针。

使用malloc分配的内存必须使用free释放;而使用new分配并构造的对象必须使用delete释放和销毁。

new运算符在分配内存时会进行初始化(对于类类型会调用构造函数),而malloc不会。

在C++中,推荐使用new和delete进行动态内存管理,因为它们更符合C++的面向对象特性。然而,在处理某些与C语言接口的代码或需要精确控制内存布局的场景中,malloc和free仍然是有用的。

20、静态多态和动态多态

静态多态,又称编译期多态,是指在编译时就能确定对象的类型和方法调用的多态性。

实现方式:

函数重载和运算符重载。在C++中,函数重载是指在同一个作用域内,可以声明多个具有相同名字但参数列表不同的函数。运算符重载则是对已有的运算符进行重新定义,使其能够用于用户自定义的类型。

特点:

效率较高:由于静态多态在编译时确定,编译器可以进行优化,提高程序运行效率。

适配性和松耦合性:通过模板和特化等技术,静态多态可以处理不同类型的数据,实现代码的复用和扩展。

泛型设计:静态多态为C++带来了泛型设计的概念

二、动态多态是指在运行时才能确定对象的类型和方法调用的多态性。

实现方式:

继承和虚函数来实现。一个基类中的成员函数可以被声明为虚函数,这意味着该函数在派生类中可以被重写(Override)。当使用基类指针或引用来调用虚函数时,程序会在运行时根据实际对象的类型来确定调用哪个函数。

特点:

灵活性高:动态多态允许程序在运行时根据对象的实际类型来选择合适的方法,提高了程序的灵活性和可扩展性。

接口与实现分离:通过虚函数和继承,动态多态实现了接口与实现的分离,使得代码更加清晰和易于维护。

处理异质对象集合:动态多态可以处理同一继承体系下的异质对象集合,实现多态性。

示例:

在C++中,可以通过继承和虚函数实现动态多态。例如,一个基类中包含一个虚函数,派生类重写该虚函数。当使用基类指针指向派生类对象并调用虚函数时,程序会调用派生类中的重写函数。

三、静态多态与动态多态的比较

本质区别:

静态多态在编译时确定对象的类型和方法调用,由模板具现完成。

动态多态在运行时确定对象的类型和方法调用,由继承和虚函数实现。

接口形式:

静态多态的接口是隐式的,以有效表达式为中心,多态通过模板具现在编译期完成。

动态多态的接口是显式的,以函数签名为中心,多态通过虚函数在运行期实现。

优缺点:

静态多态的优点包括效率高、适配性强、支持泛型设计等;缺点包括调试困难、编译耗时、代码膨胀等。

动态多态的优点包括灵活性高、接口与实现分离、处理异质对象集合等;缺点包括运行期绑定导致一定的运行时开销、编译器无法对虚函数进行优化等。

综上所述,静态多态和动态多态各有优缺点,应根据具体的应用场景和需求来选择合适的多态实现方式。

21、内存分区

在C语言中,程序的内存布局通常被划分为几个不同的区域,每个区域都有其特定的用途和管理方式。以下是对C语言内存分区的详细解释:

代码区(Text Segment):

代码区也被称为文本段或代码段。

这个区域存储了程序的机器指令,即CPU要执行的代码。

代码区通常是只读的,以防止程序意外地修改自己的指令。

当程序被加载到内存中时,代码区的内容从可执行文件中复制而来。

全局/静态数据区(Data Segment):

全局/静态数据区包含了全局变量、静态变量和常量数据。

全局变量和静态变量在程序的整个生命周期内都存在,而常量数据则用于存储字符串常量等不变的值。

这个区域在程序开始执行前就被初始化,并在程序结束时被释放。

它分为已初始化和未初始化两个部分:

已初始化数据区(Data Segment):存储已初始化的全局变量和静态变量。

未初始化数据区(BSS Segment):存储未初始化的全局变量和静态变量,这些变量在程序开始执行前被自动初始化为零。

堆区(Heap):

堆区用于动态内存分配。

程序员可以使用如malloc、calloc、realloc等函数在堆上分配内存,并使用free函数释放内存。

堆区的大小在程序运行时是可变的,由程序员控制。

如果程序员忘记释放已分配的内存,可能会导致内存泄漏。

栈区(Stack):

栈区用于存储局部变量和函数调用信息(如函数参数、返回值地址、局部变量指针等)。

栈的大小在程序编译时确定,并在程序运行时由系统自动管理。

每当函数被调用时,系统会在栈上为该函数的局部变量和调用信息分配空间;当函数返回时,这些空间会被自动释放。

栈的访问速度非常快,因为栈内存通常是连续分配的。

这些内存分区共同构成了C语言程序的内存布局。程序员需要了解这些分区的特性和用途,以便正确地管理内存和编写高效的C语言程序。同时,也要注意避免内存泄漏、缓冲区溢出等常见的内存管理错误。

22、文件编译过程

文件编译的过程是将源代码转换为可执行文件的过程,这个过程通常包括预处理、编译、汇编和链接四个阶段。

一、预处理(Preprocessing)

主要任务是对源代码进行初步的处理,为后续的编译阶段做准备。

处理头文件:通过#include指令,将所需的头文件内容插入到源文件中,形成一个整体的源代码文件。头文件通常包含函数声明、宏定义、类型定义等。

宏替换:将所有定义的宏进行替换,例如将所有出现的宏名替换为对应的宏定义内容。宏定义通常使用#define指令进行。

条件编译:根据预处理指令(如#ifdef、#ifndef、#if、#elif等)的条件判断,选择性地编译代码段。

删除注释:删除所有的注释内容,包括单行注释(//)和多行注释(/* */)。

预处理后的结果通常是一个中间文件,这个文件包含了经过宏替换、条件编译等处理后的源代码。

二、编译(Compilation)

编译是编译过程中的核心阶段,主要任务是将预处理后的源代码转换为汇编语言。

词法分析:将源代码划分为一个个的标记(token),如关键字、标识符、运算符等。

语法分析:根据语法规则,将标记组合成语法树,并检查代码是否符合语法规范。

语义分析:对语法树进行语义检查,包括类型检查、变量声明检查等。如果源代码有语法或语义错误,编译器会报错并停止编译过程。

编译后的结果通常是汇编代码,这些代码包含了程序的基本逻辑和运算指令。

三、汇编(Assembly)

汇编是将编译生成的汇编代码转换为机器指令的过程。

符号解析:将变量和函数引用与其定义进行关联,生成符号表。

生成机器码:将汇编指令翻译成机器指令,并生成目标文件(通常是.o或.obj文件)。目标文件包含了程序的可执行代码以及相关的调试信息。

四、链接(Linking)

链接是将多个目标文件和所需的库文件链接在一起,生成最终的可执行文件的过程。

符号解析:将各个目标文件中的符号引用与其定义进行关联,解决符号引用问题。

重定位:将目标文件中的地址引用转换为实际的内存地址。

合并代码和数据:将各个目标文件中的代码和数据合并到一起,生成最终的可执行文件。

链接过程中还会处理程序的静态库和动态库依赖关系,确保程序在运行时所需的库文件已经被正确地加载。

总结

通过预处理、编译、汇编和链接这四个阶段,源代码最终被转换为可执行文件,并在计算机上运行。在实际的软件开发中,这些阶段通常由编译器和链接器自动完成。了解文件编译的过程有助于程序员更好地理解程序的底层机制和运行原理,从而更好地优化代码和提高编译效率。

23、虚拟内存

虚拟内存是计算机系统内存管理的一种技术,它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上这部分内存是被分隔成多个物理内存碎片的,有时部分数据还会暂时存储在外部磁盘存储器上,在需要时进行数据交换。以下是对虚拟内存的详细解释:

一、定义与概念

虚拟内存是操作系统用来扩展可用内存容量的一种技术。它通过将部分数据暂时存储在硬盘上,使得超出物理内存限制的数据也能被有效使用。每个程序都使用虚拟地址空间来访问内存,而非直接访问物理内存。虚拟内存管理器负责将虚拟地址转换为物理地址。

二、工作原理

虚拟内存的核心在于分页(Paging)技术。操作系统将内存划分为固定大小的块,称为页面(Page),每一页通常为4KB。在实际运行时,操作系统会将所需的页面从虚拟内存调入物理内存。当某个页面不再需要时,它会被换出(Swapped Out)到虚拟内存。以下是虚拟内存工作原理的详细步骤:

地址映射:每个进程都有自己的页面表,用于记录虚拟地址和物理地址的映射关系。当进程访问内存时,CPU会使用页面表来转换虚拟地址为物理地址。

页面调度:当程序需要访问一个页面时,虚拟内存管理器会检查该页面是否已经在物理内存中。如果已经在物理内存中,则直接访问;如果不在,则发生页面错误(Page Fault)。

页面错误:当进程访问的页面不在物理内存中时,会发生页面错误。操作系统会暂停进程,将所需的页面从虚拟内存调入物理内存(通常是从硬盘上的页面文件中读取),然后恢复进程的执行。

页面置换:当物理内存已满时,操作系统会选择不常用的页面进行置换,将其换出到虚拟内存(即硬盘上的页面文件中),从而腾出空间给新的页面。页面置换算法有多种,如最近最少使用(LRU)等。

三、优点与缺点

优点

扩展内存空间:虚拟内存使得计算机能够运行比物理内存更大的应用程序,有效扩展了系统的内存容量。

提高多任务处理能力:通过虚拟内存,操作系统可以在有限的物理内存上运行多个程序,提高了系统的多任务处理能力。

提高内存利用率:虚拟内存允许程序使用比物理内存更多的内存,而不会因此而崩溃。同时,通过页面置换算法,可以更有效地利用物理内存空间。

简化内存管理:虚拟内存为程序员提供了一个更大的、连续的地址空间,简化了内存管理的工作。

缺点

占用一定的物理硬盘空间:虚拟内存需要在硬盘上存储页面文件,因此会占用一定的物理硬盘空间。

加大了对硬盘的读写:由于页面置换和页面调度等操作,虚拟内存会加大对硬盘的读写频率,可能会影响硬盘的寿命和性能。

设置不当会影响整机稳定性与速度:如果虚拟内存设置不当(如页面文件大小设置不合理、虚拟内存与系统设在同一分区内等),可能会影响整机的稳定性和速度。

四、应用场景

大型数据处理:在处理大数据集或运行大型应用程序时,虚拟内存可以提供更大的内存空间,避免内存不足的问题。

多任务操作:虚拟内存允许同时运行多个应用程序,提高了多任务处理能力,适用于需要同时运行多个程序的场景。

内存保护:通过虚拟内存提供的内存保护机制,可以防止不同进程之间的内存互相干扰,从而提高系统稳定性和安全性。

五、配置与优化

根据内存大小和电脑用途设定:虚拟内存的设定主要根据电脑的内存大小和用途来设定。一般来说,可以让操作系统自动分配管理虚拟内存,它能根据实际内存的使用情况动态调整虚拟内存的大小。

避免与系统设在同一分区内:为了避免系统在此分区内进行频繁的读写操作而影响系统速度,最好将虚拟内存设置在其它分区中磁盘剩余空间较大而又不常用的盘中(如D、E盘)。

合理配置虚拟内存大小:一般默认的虚拟内存大小是取一个范围值,但最好给它一个固定值以减少磁盘碎片的产生(但需要注意的是固态硬盘不会产生磁盘碎片)。具体数值可以根据物理内存大小来定。

选择速度较快的硬盘或SSD:为了提高虚拟内存的性能,可以选择速度较快的硬盘或固态硬盘(SSD)来存储页面文件。

综上所述,虚拟内存是计算机系统内存管理的一种重要技术,它通过分页技术和页面置换算法实现了内存扩展和多任务处理等功能。虽然虚拟内存有一定的缺点和限制,但通过合理的配置和优化,可以充分发挥其优势并提高系统的性能和稳定性。

24、浏览器输入网址后执行的过程?

当在浏览器中输入网址并按下回车键后:

DNS解析:浏览器首先将输入的网址发送给DNS服务器,以获取该网址对应的IP地址。DNS服务器会查询其数据库,在找到匹配的域名时返回对应的IP地址给浏览器。

TCP连接建立:浏览器使用获取到的IP地址与服务器建立TCP连接。这涉及到使用TCP三次握手的过程,确保客户端与服务器之间的可靠连接。

发起HTTP请求:一旦建立了TCP连接,浏览器会发送HTTP请求到服务器。请求包含请求方法(例如GET、POST)、请求的URL、HTTP版本以及其他可能的请求头信息,如用户代理、Cookie等。

服务器处理请求:服务器收到HTTP请求后,会根据请求的URL和其他请求信息来处理请求。服务器可能会读取请求中的参数,查询数据库,执行相应的逻辑处理,并生成HTTP响应。

HTTP响应:服务器生成完整的HTTP响应后,将其返回给浏览器。响应包括一个状态码表示请求的结果(例如200表示成功,404表示资源未找到等),响应的内容,以及其他响应头信息,如Content-Type、Content-Length等。

接收和解析响应:浏览器接收到服务器的HTTP响应后,开始解析响应。它会检查状态码,根据响应头中的Content-Type确定响应内容的类型,并将响应的内容保存下来。

渲染页面:如果响应的内容是HTML页面,浏览器会开始解析HTML,并构建DOM树。然后,将CSS文件加载和解析为样式规则,并将其应用于DOM树,生成渲染树。最后,浏览器使用渲染树将页面内容绘制到用户的屏幕上。

关闭TCP连接:一旦页面完全加载并渲染完成,浏览器会关闭与服务器之间的TCP连接。但是,如果页面中存在其他的资源(如图片、脚本、样式表等),浏览器可能会继续发送HTTP请求获取这些资源。

25、进程和线程区别

进程是操作系统资源分配的最小单元,一个进程拥有的资源有字节的堆、栈、虚存空间(页表)等

可以看作是一个类或PCB(进程控制块)的结构体,进程是操作系统堆一个正在运行的程序的一种抽象,可以把进程看作程序运行的一次运行过程

程序是在一个静态磁盘上的一个可执行文件

进程是将可执行文件加载到系统中,加载就是将信息放在内存中,分配资源并执行指令,

进程本质PCB:代表一个实实在在运行着的程序,也就是进程

包括PID:进程ID

进程状态:新建状态,就绪,运行,阻塞,销毁

优先级:决定进程的执行顺序

记账信息:记录CPU调用次数和执行间隔

上下文信息:保存本次执行状态

一组内存:指定进程需要使用的资源

线程:被包含在进程中,是进程中实际运行的单位

一个进程可以并发多个线程,每个线程执行不同的任务,是操作系统进行运算调度的最小单元,进程中包含了线程,线程属于进程

每个进程有自己的内存和资源,一个进程中的线程会共享这些内存和资源

26、进程间的通信方式?线程的通讯方式呢?怎么实现

管道:半双工通信,

从内核中划出一片空间用作缓存,一个进程往这片缓存中写入数据,另一个进程读取这个缓存中的数据,分为匿名管道和命名管道,匿名管道是半双工通信,数据只能在管道中单向流动,如果想要进行双向通信,则需要建立两条管道,且一般只用于父子进程之间的通信

一个进程采用pipe()函数,创建管道,该函数返回两个文件描述符,一个是写描述符,一个是读描述符

通过fork()创建一个子进程,然后实现父子进程之间的通信,

信号:是一种软中断,产生方式有两种,一种为硬件产生,即采用终端设备比如键盘来产生信号,另一种是通过调用系统函数给一个进程发送信号

进程收到信号之后并不会立刻执行,而是在cou由内核态转变为用户态之前会检查是否有未处理的信号

消息队列:进程给进程发送数据时,只需要即将数据放在消息队列中便可以立即返回而不需要阻塞

消息队列是保存在内核中的消息链表,通信双方会约定好收发数据的格式,而不是无格式的字节流

共享内存:可以理解为操作系统内核所划分出的一段物理地址空间,由于每一个进程都有自己的虚拟地址空间,且通过页表完成地址的映射,所以可以通过借助页表完成进程中的一段虚拟地址空间和共享内存的映射

信号量:当使用共享内存的通信方式,如果有多个进程同时往共享内存写入数据,有可能先写的进程的内容被其他进程覆盖了,因此需要一种保护机制,信号量本质是一个整型的计数器,用于实现进程间的互斥和同步

信号量代表资源的数量,操作信号量的方式有两种:

P操作:将信号量减一,相减后信号量如果小于0,则表示资源已经被占用了,进程需要阻塞等待,如果大于等于0,则说明还有资源可用,进程可以正常执行

V操作:将信号量加一,相加后如果小于等于0,则表明当前有进程阻塞,于是将该进程唤醒,如果大于0,则表明当前没有阻塞的进程

26、死锁?怎么产生的?

两个或多个并发进程中,如果每个进程持有某种资源而又都等待着别的进程释放它或它们现在保持着的资源,否则就不能向前推进,此时每个进程都占用了一定的资源,但又都不能向前推进,称这一组进程产生了死锁

产生原因:系统资源不足,进程推进顺序非法

必要条件:

互斥条件---涉及的资源是非共享的

不剥夺条件---进程所获得的资源在未使用完毕之前不能被其他进程强行夺走

部分分配---进程每次申请它所需的一部分资源,在等待新资源的同时继续占用已分配到的资源

环路条件

线程状态,它们是怎么转换的?

新建状态:新创建了一个线程对象

就绪状态:线程对象创建后,只等待获取cpu的使用权

运行状态:就绪状态的线程获得了cpu执行程序代码

阻塞状态:线程因某种原因放弃cpu使用权,暂时停止运行,直到线程进入就绪状态,才有机会转到运行状态

死亡状态:线程执行完了或因异常退出了run方法,该线程结束生命周期

27、设计模式?

工厂模式:定义创建对象的接口,封装对象的创建,使得具体化类的工作延迟到了子类中

Return new ConcreteProduct();

仅局限于一类的类,

AbstactFactory模式:抽象工厂模式

28、右值引用

左值可以取地址,可以修改,可以放在等号两边,右值不可以取,只能放在右边,没有名称

区分:放在等号左边的是左值,右边的是右值

Int a=3;//a是左值,3是右值

Int b=a;//a,b都是左值

Int &&b=a;//右值引用左值不行

右值引用&&

右值引用必须初始化:int &&a=10;

不能用左值初始化

当对右值加上引用后可以修改值也可以修改地址,从功能上升为左值,右值引用的本质是不用拷贝的左值,

引用的目的:传递参数有两种方式:值传递和引用传递

左值引用:函数传参,函数返回值,相比于值传递减少了拷贝次数

当返回值为右值引用时,会把返回的临时变量中的内存据为己有,仍保持了有效性,避免拷贝

29、MVC

MVC模式,即Model-View-Controller模式,是一种经典的软件设计模式。

MVC模式旨在将应用程序的输入、处理和输出分开,使得数据(Model)、视图(View)和控制逻辑(Controller)相互独立,从而提高代码的可扩展性、可复用性、可维护性以及灵活性。

二、组成部分

模型(Model):

是应用程序中的数据部分,表示应用程序中的状态和行为。

负责处理应用程序的数据逻辑和业务规则。

可以是一个或多个JavaBean对象,它们封装了数据的属性和对这些数据的方法操作。

视图(View):

是用户的操作界面,负责显示应用程序的用户界面。

对于Web应用来说,视图可以是JSP页面、HTML页面或其他类型的用户界面。

视图只负责数据的显示和采集,而不包含任何业务逻辑或数据处理。

控制器(Controller):

处理从视图层发送的请求,并选取模型层的业务模型完成响应的业务实现。

控制器接收用户的输入,并将其转化为对模型或视图的操作。

在Web应用中,控制器通常是一个Servlet对象,它接收用户的请求,并根据请求来调用相应的模型和视图。

三、工作原理

用户通过视图层与应用程序进行交互,发送请求。

控制器接收用户的请求,并根据请求的类型和参数来选择相应的模型和视图。

控制器调用模型层来处理用户请求的数据逻辑和业务规则。

模型层处理完数据后,将结果返回给控制器。

控制器选择相应的视图来显示处理结果。

视图从模型中获取数据,并将其展示给用户。

四、优点

模块化:MVC模式将应用程序划分为三个独立的模块,提高了代码的可维护性和可扩展性。

灵活性:由于视图、模型和控制器的相互独立,可以方便地对其中任何一部分进行更改、替换或扩展,而不会影响到其他部分。

可重用性:模型可以被多个视图共享,提高了代码的重用性。

分离关注点:MVC模式将用户界面的设计、数据逻辑的处理和业务流程的控制分离开来,使得开发人员可以更加专注于各自的领域。

五、应用场景

MVC模式广泛应用于Web应用程序的开发中,特别是在Java Web开发中,如使用Struts、Spring MVC等框架。此外,MVC模式还可以应用于桌面应用程序、移动应用程序以及游戏开发等领域。

综上所述,MVC模式是一种强大的软件设计模式,它通过分离应用程序的输入、处理和输出,使得代码更加清晰、可维护和可扩展。在实际应用中,可以根据具体的应用场景和需求来选择是否使用MVC模式以及如何使用它。

30、分布式项目中客户端与服务端通信流程

一、建立连接

首先需要建立连接。这一步骤通常通过TCP/IP协议完成,包括三次握手以确保双方能够稳定、可靠地通信。

二、发送请求

客户端准备请求:

客户端根据业务需求,准备要发送的请求数据。

请求数据通常包括请求头(如方法、路径、协议版本等)和请求体(如请求参数、业务数据等)。

序列化请求数据:

为了在网络中传输,请求数据需要被序列化为二进制格式。

序列化过程将数据结构转换为一系列字节,以便通过网络发送。

发送请求:

客户端通过已建立的连接,将序列化后的请求数据发送给服务端。

这一步骤通常使用Socket编程或类似机制完成。

三、接收响应

服务端接收请求:

服务端通过监听端口接收来自客户端的请求数据。

接收到的数据被反序列化为原始数据结构,以便后续处理。

处理请求:

服务端根据请求的内容和方法,执行相应的业务逻辑。

这一步骤可能涉及数据库查询、计算、文件操作等。

准备响应:

服务端将处理结果封装为响应数据。

响应数据通常包括状态码、响应头和响应体(如处理结果、错误信息等)。

序列化响应数据:

为了将响应数据发送给客户端,服务端需要将其序列化为二进制格式。

发送响应:

服务端通过已建立的连接,将序列化后的响应数据发送给客户端。

四、处理响应

客户端接收响应:

客户端通过已建立的连接,接收来自服务端的响应数据。

反序列化响应数据:

客户端将接收到的二进制数据反序列化为原始数据结构。

处理响应结果:

客户端根据响应数据的内容,执行相应的业务逻辑。

这一步骤可能涉及更新UI、存储数据、处理错误等。

五、断开连接

在完成通信后,客户端与服务端通常会断开连接,以释放资源。这一步骤通常通过四次挥手等过程完成。

六、通信过程中的注意事项

异常处理:

在通信过程中,可能会出现网络故障、超时、数据错误等异常情况。

客户端和服务端需要设计合理的异常处理机制,以确保系统的稳定性和可靠性。

安全性:

分布式系统中的通信通常涉及敏感数据的传输。

因此,需要采用加密、签名、身份验证等安全措施来保护通信过程和数据安全。

性能优化:

为了提高通信效率,可以采用压缩、缓存、异步通信等技术手段来优化通信过程。

综上所述,分布式项目中客户端与服务端之间的通信流程是一个复杂而重要的环节。通过合理的设计和实现,可以确保系统的稳定性、可靠性和高效性。

31、tcp相比于udp的区别

TCP(传输控制协议)和UDP(用户数据报协议)都是运输层(第四层)的协议,

一、面向连接与无连接

TCP:面向连接的协议。在数据传输之前,需要通过三次握手建立连接,确保通信双方准备就绪。数据传输结束后,需四次挥手释放连接。这种面向连接的特性使得TCP能够提供可靠的通信服务。

UDP:无连接的协议。在数据传输之前,不需要建立连接,直接发送数据包。每个UDP数据报都是独立的,不会保存连接状态。这种无连接的特性使得UDP的传输效率更高,但可能牺牲一定的可靠性。

二、可靠性

TCP:提供可靠的数据传输服务。通过序列号、确认应答、重传机制等手段,确保数据能够按照发送的顺序正确、无差错地到达接收方。如果数据包在传输过程中丢失或损坏,TCP会触发重传机制,直到数据成功传输为止。

UDP:不保证数据的可靠传输。没有重传机制和顺序控制机制,发出的数据包一旦发生丢失就无法恢复,也无法保证接收方接收到数据的顺序一定是发送方发送的顺序。因此,UDP的传输可靠性较低,但传输速度更快。

三、拥塞控制

TCP:具有拥塞控制机制。可以根据网络的拥塞程度动态调整发送数据的速率,避免网络拥塞导致数据丢失和延迟增加。TCP的拥塞控制机制包括慢启动、拥塞避免、快重传和快恢复等算法。

UDP:没有内置的拥塞控制机制。它会将所有数据发送到网络上,无论网络状态如何。这可能导致网络拥塞,但在某些实时性要求高的应用场景中,UDP的这种特性反而能够提高其传输效率。

四、传输效率

TCP:由于需要建立连接、进行确认应答和重传等操作,TCP的传输效率相对较低。但这也保证了其数据传输的可靠性和稳定性。

UDP:没有建立连接的过程,也不需要确认应答和重传等操作,因此UDP的传输效率更高。这使得UDP在实时性要求高的应用场景中更具优势。

五、使用场景

TCP:适用于需要可靠数据传输的场景,如文件传输、电子邮件等。在这些场景中,数据的完整性和顺序性至关重要。

UDP:适用于实时性要求高、但对数据可靠性要求不高的场景,如实时视频、音频传输、在线游戏等。在这些场景中,数据的实时性更为重要,而少量数据的丢失或顺序错乱是可以容忍的。

5、为什么项目中采用的是tcp而不是udp

广泛应用:

TCP被广泛使用在许多重要的应用层协议中,如HTTP、HTTPS、FTP和SMTP等。这些协议的成熟和普遍使用使得TCP成为了互联网的基石。

相比之下,UDP虽然在一些特定场景下(如实时音视频传输、在线游戏和广播应用)具有优势,但在需要确保数据完整性和可靠性的场景中,TCP仍然是首选。

开发者偏好和用户期望:

许多开发者选择TCP是因为它更容易管理和调试连接的可靠性,而不必担心手动处理重传和顺序问题。

在许多情况下,用户更关心数据的可靠传输,而不是速度。例如,在电子邮件和文件下载中,数据完整性和可靠性是关键因素。

综上所述,虽然UDP在速度上具有优势,但由于TCP在可靠性、流量控制、拥塞控制以及广泛应用方面的优势,使得TCP在许多项目中仍然是主流选择。

32、RAII

RAII(Resource Acquisition Is Initialization)设计思想,即"资源获取即初始化",是C++中的一种管理资源、避免泄漏的惯用法。它的核心思想是将资源的管理(如动态内存的分配和释放、文件句柄的打开和关闭等)与对象的生命周期紧密绑定。具体来说,资源在对象的构造函数中被分配(或获取)并初始化,而在对象的析构函数中被释放(或回收)。这种方式确保了资源总是被正确管理,即使在发生异常的情况下也能保证资源被正确释放,从而避免了资源泄漏等问题。

一、资源分配与初始化

在对象的构造函数中,通过适当的操作(如new操作符、文件打开函数等)分配或获取所需的资源。

将这些资源保存在对象的成员变量中,以便在对象的其他成员函数中使用。

二、资源释放与析构

在对象的析构函数中,通过适当的操作(如delete操作符、文件关闭函数等)释放或回收在构造函数中分配或获取的资源。

析构函数会在对象生命周期结束时被自动调用,从而确保资源总是被正确释放。

三、异常安全性

由于RAII利用了C++中对象自动调用析构函数的特性,即使在发生异常的情况下,也能保证资源被释放。

这使得使用RAII管理资源的代码更加健壮和可靠。

四、智能指针与RAII

智能指针是RAII思想的一个重要应用。智能指针类封装了常规指针,并在内部使用引用计数或其他机制来自动管理内存的分配和释放。

当智能指针对象不再被使用时(如超出作用域、被显式删除或被赋予新值等),其所指向的内存会被自动释放,从而避免了内存泄漏问题。

五、RAII的优势

简化资源管理:通过将对资源的管理封装在对象中,简化了资源管理的代码。

提高代码可读性:使用RAII可以使代码更加清晰和易于理解,因为资源的分配和释放都在对象的构造函数和析构函数中完成。

增强异常安全性:即使在发生异常的情况下,也能保证资源被正确释放。

六、RAII的应用场景

RAII广泛应用于C++等支持对象导向编程的语言中,用于管理各种资源,如动态内存、文件句柄、网络连接等。

在实际开发中,可以使用标准库中的智能指针(如std::unique_ptr、std::shared_ptr等)来方便地实现RAII。

综上所述,RAII设计思想是一种非常有效的资源管理方式,它通过将资源的管理与对象的生命周期紧密绑定,确保了资源的正确分配和释放。

遵循RAII原则:RAII(Resource Acquisition Is Initialization)是一种管理资源的技术,它要求资源(如内存、文件句柄等)的获取和释放与对象的生命周期绑定。在对象的构造函数中分配资源,在析构函数中释放资源。

33、分布式系统

C++中实现分布式系统是一项复杂的任务,通常涉及网络通信、数据同步、并发处理等多个方面。

  1. 网络通信

分布式系统的核心在于节点之间的通信。

Boost.Asio:一个跨平台的C++库,用于网络和底层I/O编程。支持TCP、UDP、串口等通信方式。

ZeroMQ:一个高性能的异步消息库,支持多种传输协议(如TCP、IP多播、in-process等)。

gRPC:高性能、开源和通用的RPC框架

  1. 数据同步和一致性

确保分布式系统中各个节点之间的数据一致性是关键。

分布式锁:使用Redis或ZooKeeper实现分布式锁,确保在多个节点之间对共享资源的互斥访问。

分布式事务:如Google的Spanner和Percona的XtraDB Cluster等数据库系统提供了分布式事务的支持。

一致性算法:如Paxos、Raft等,用于确保分布式系统中的日志或配置数据的一致性。

  1. 并发处理

C++11及更高版本提供了多线程支持,但处理分布式系统中的并发任务通常需要更复杂的机制。

C++标准库中的线程和同步原语:如std::thread、std::mutex、std::condition_variable等。

任务队列:使用线程池和任务队列来管理并发任务,如Intel TBB(Threading Building Blocks)或Boost.Thread。

  1. 序列化与反序列化

在分布式系统中,数据需要在不同节点之间传输,通常需要进行序列化和反序列化。

Protocol Buffers:Google开发的一种与语言无关、平台无关的可扩展机制,用于序列化结构化数据。

Boost.Serialization:一个C++库,用于序列化和反序列化C++对象。

JSON/XML:简单的文本格式,便于调试和跨语言使用。

  1. 集群管理与部署

管理和部署分布式系统的集群节点也是一个重要方面。

Kubernetes:一个开源的容器编排和管理平台,支持自动化部署、扩展和管理容器化应用程序。

Docker:一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。

34、jsoncpp

JsonCpp是一个用于解析和生成JSON数据的C++库,它提供了DOM风格的API来处理JSON数据,可以将整个JSON文档加载到内存中,然后像操作树结构一样操作它。

JsonCpp的序列化过程通常涉及创建一个Json::Value对象,然后使用它的方法来构建JSON结构,最后使用Json::StreamWriterBuilder将其转换为字符串。

二、工作模式

基于对象的模型:将JSON结构映射到C++对象,适合于需要深度访问JSON树的情况。在这种模式下,你可以像操作C++对象一样操作JSON数据,这使得数据访问和操作更加直观和方便。

基于值的模型:更适用于简单的读写操作。在这种模式下,JsonCpp内部实现了自动内存管理,使用更为简便。你可以直接使用JsonCpp提供的Json::Value类型来存储和操作JSON数据。

三、使用方法

使用API:

使用Json::Reader类来解析JSON字符串。

使用Json::Value类来表示和操作JSON数据。

使用Json::StreamWriterBuilder类来将Json::Value对象序列化为JSON字符串。

四、应用场景

配置文件读取:JsonCpp可以方便地用于读取或保存应用程序的配置信息,因为JSON格式清晰易读,相比XML更简洁。

网络通信:在网络API调用中,JsonCpp常用于解析返回的JSON数据,或者将C++对象转换为JSON字符串发送给服务器。

数据分析:在数据分析领域,JsonCpp可以作为数据导入导出工具,帮助你将JSON数据轻松转换为C++中的结构化数据。

日志记录:生成结构化的JSON格式日志,便于后续进行自动化处理和分析。

相关推荐
浮游本尊17 分钟前
Java学习第22天 - 云原生与容器化
java
沐怡旸43 分钟前
【底层机制】std::unique_ptr 解决的痛点?是什么?如何实现?怎么正确使用?
c++·面试
感哥1 小时前
C++ 内存管理
c++
佛祖让我来巡山1 小时前
深入理解JVM内存分配机制:大对象处理、年龄判定与空间担保
jvm·内存分配·大对象处理·空间担保·年龄判定
渣哥2 小时前
原来 Java 里线程安全集合有这么多种
java
间彧2 小时前
Spring Boot集成Spring Security完整指南
java
间彧3 小时前
Spring Secutiy基本原理及工作流程
java
Java水解4 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆6 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学6 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端