c++面经总结

C++基础语法

C++和c的区别

  • c++中new和delete是对内存分配的运算符,取代了c中的malloc和free

  • 标准c++中的字符串类取代了标准c函数库头文件中的字符数组处理函数(c中没有字符串类型).

  • 在c++中,允许有相同的函数名,不过他们的参数类型不能完全相同,这样这些函数就可以相互区分开来。而在c语言中是不允许的。也就是c++可以重载,c不允许.

  • c++用来做控制态输入输出的iostream类库代替了c中的stdio。

  • c++语言中,允许变量定义语句在程序中的任何地方,只要在是使用它之前就可以;而c语言中必须要在函数开头部分的。c++不允许重复定义变量,c语言也是做不到的。

  • c++中,除了值和指针之外,还加了引用。引用变量是其它变量的一个别名,我们可以认为他们只是名字不相同,其它都是相同的

1.c++为什么支持函数重载,c语言不支持函数重载

  • C++代码产生函数符号的时候,函数名+参数列表类型组成的!

  • C代码产生函数符号的时候,函数名来决定! I

重载和重写的区别

  • 重载

    • 重载是指在同一范围内定义中的同名成员函数才存在重载关系。主要特点就是函数名相同,参数类型和数目有所不同,不能出现参数个数和类型均相等的情况,仅仅依靠返回值不同来区分的函数。重载和函数成员是否是虚函数无关。

  • 重写

    • 重写指的是派生类中覆盖基类中的同名函数,重写就是重写函数体,要求基类函数必须是虚函数且:

      • 与基类的虚函数有相同的参数个数

      • 与基类的虚函数有相同的参数类型

      • 与基类的虚函数有相同的返回值类型

      举例子

       //父类class A{public:virtual int fun(int a){}}//子类classB : class A{public://重写,一般加override可以确保是重写父类的函数virtual int fun(int a)override{}}

重载与重写的区别:

  • 重写是父类和子类之间的垂直关系,重载是不同函数之间的水平

  • 重写要求参数列表相同,重载则要求参数列表不同,返回值不要求

  • 重写关系中,调用方法根据对象类型决定,重载根据调用时实参表与形参表的对应关系来选择函数体

讲一下c++的三中继承

  • public继承

    • 公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,都保持原有的状态,而基类的私有成员任然是私有的,不能被这个派生类的子类所访问。

  • protected继承

    • 保护继承的特点是基类的所有公有成员和保护成员都称为派生类的保护成员,并且只能被它的派生类成员函数或友元函数所访问,基类的私有成员仍然是私有的,

  • private继承

    • 私有继承的特点是基类的所有公有成员和保护成员都为派生类的私有成员,并不被他的派生子类所访问,基类的成员只能由派生类访问,无法继续往下访问

指针和引用的区别

  • 指针是一个变量,存储的是一个地址,引用跟原来的变量实质上是同一个东西,是变量的别名。

  • 指针有多级,而引用只有一级

  • 指针可以为空,但是引用不能为空且定义时必须初始化

  • 指针在初始化后还可以改变指向,但是引用不可以

  • sizeof指针得到的是指针的大小,引用得到是所指向的大小

结构体对齐讲一讲? int double int的大小

抽象类和普通类有什么区别

 //动物的基类  泛指 类-》抽象一个实体的类型//定义Animal 的初衷 并不是让Anmail抽象某个实体的类型//作用://1. string _name 让所有动物实体类通过继承Animal直接复用该属性//2. 给所有的派生类保留同一的覆盖/重写接口//拥有纯虚函数的类 就是抽象类// 抽象类不能实例化对象,但是可以定义指针和引用的变量class Animal{public:Animal(string) : _name(name){}virtual void bark() = 0;//纯函数protected:string _name;};class Cat : public Animal{public:Cat(string name) :Animal(name){}void bark() { cout<<_name<<"bark :miao miao"<<endl;}};class Dog : public Animal{public:Dog (string name) :Animal(name){}void bark() { cout<<_name<<"bark :wang wang"<<endl;};//这个如果每个都加下面的话就太麻烦了  如果新新添加或者删除的话就不好了/*void bark(Cat &cat){cat.bark();}void bark(Dog &dog){dog.bark();}*/void bark(Animal *p){p -> bark();//Anmail :: bark 虚函数,动态绑定le//p->cat cat vftable &Cat::bark}//这个就是多态int main(){Cat cat("秘密");Dog dog("jj");bark(&cat);bark(&dog);}

一般把什么设置成抽象类

对多态的理解

  • 定义:同一事物表现出不同事务的能力,即向不同对象发送同一信息,不同的对象在接受时会产生不同的行为(重载实现编译时多态,虚函数实现运行时多态)

  • 功能:多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作;

  • 简单一句话:允许将子类类型的指针赋值给父类类型的指针。

实现多态有两种方式 1.覆盖(override):是指子类重新定义父类的虚函数的做法

2.重载(overload):是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)

例如:

基类是一个抽象———人,那学生、运动员也是人,而使用这个抽象对象既可以表示学生、也可以表示运动员。

 class Animal{public:Animal(string) : _name(name){}virtual void bark() {}//这个是叫的虚函数protected:string _name;};class Cat : public Animal{public:Cat(string name) :Animal(name){}void bark() { cout<<_name<<"bark :miao miao"<<endl;}};class Dog : public Animal{public:Dog (string name) :Animal(name){}void bark() { cout<<_name<<"bark :wang wang"<<endl;};//这个如果每个都加下面的话就太麻烦了  如果新新添加或者删除的话就不好了/*void bark(Cat &cat){cat.bark();}void bark(Dog &dog){dog.bark();}*/void bark(Animal *p){p -> bark();//Anmail :: bark 虚函数,动态绑定le//p->cat cat vftable &Cat::bark}//这个就是多态int main(){Cat cat("秘密");Dog dog("jj");bark(&cat);bark(&dog);}
 std::function 可以容纳不同签名的可调用对象,实现多态性。#include <iostream>#include <functional>​void printMessage(const std::string& message) {std::cout << message << std::endl;}​void printNumber(int number) {std::cout << "Number: " << number << std::endl;}​int main() {// 使用 std::function 容纳不同签名的函数std::function<void(const std::string&)> func1 = printMessage;std::function<void(int)> func2 = printNumber;​// 调用包装的函数func1("Hello, world!");  // 输出:Hello, world!func2(42);               // 输出:Number: 42​return 0;}

继承在讲一讲

定义:

让某种类型对象获得另一个类型对象的属性和方法。

功能:

它可以使用现有类的所有所能,并在无需重新编写原来的类的情况下对这些功能进行扩展

常见的继承有三种方式:

1、实现继承:指使用基类的属性和方法而无需额外编码的能力。

2、接口继承:指仅使用属性和方法的名额、但是子类必须提供实现的能力

3、可视继承:指子窗体(类)使用基窗体(类)的外观和实现代码的能力

例如:

将人定义为一个抽象类,拥有姓名、性别、年龄等公共属性,吃饭、睡觉等公共方法,在定义一个具体的人时,就可以继承这个抽象类,既保留了公共属性和方法,也可以在此基础上扩展跳舞、唱歌等特有方法。

 class A{public:int ma;protive:int mb;private:int mc;}class B : public A{public:int md;protive:int me;private:int mf;​}
  • 派生类从继承可以继承来所有的成员(变量和方法),除过构造函数和析构函数I

  • 派生类怎么初始化从基类继承来的成员变量呢?解答: 通过调用基类相应的构造函数来初始化

  • 派生类的构造函数和析构函数,负责初始化和清理派生类部分派生类从基类继承来的成员,的初始化和清理由谁负责呢?

    是由基类的构造和析构函数来负责

    派生类对象构造和析构的过程是: 1.派生类调用基类的构造函数,初始化从基类继承来的成员.

    2.调用派生类自己的构造函数,初始化派生类自己特有的成员.派生类对象的作用域到期了 3.调用派生类的析构函数,释放派生类成员可能占用的外部资源(堆内存,文件)

    4.调用基类的析构函数,释放派生类内存中于从基类继承来的成员可能占用的外部资源(堆内存,文件)

虚函数调用过程

  • 调用过程:

    当调用虚函数时,编译器会生成一个虚函数表,其中包含了类的虚函数地址。每个对象都包含一个指向虚函数表的指针(通常是在对象的内存布局中的一个隐藏字段)。当您通过基类指针或引用调用虚函数时,实际上是根据该对象的虚函数表来调用适当的函数。这确保了正确的函数版本被调用。

总的来说,虚函数的调用过程包括定义虚函数,创建派生类,创建对象,然后通过基类指针或引用来调用虚函数。在运行时,根据对象的实际类型确定调用的函数版本,这样可以实现多态性。这使得c++能够在运行时动态选择正确的函数,而不是在编译时静态确定。

image-20231031152839094

定义一个Base 类型的指针 如果 Base中show不是虚函数 就直接调用 静态绑定 pb Base* 如果是虚函数 *pb 指向的就是Derive派生类对象变成动态绑定

虚函数讲一讲

虚函数是指一个类中你希望重载的成员函数。当你用一个基类指针或引用指向一个继承类对象的时候,调用一个虚函数时,实际调用的是继承类的版本。虚函数的主要目的实现多态,这是面向对象编程的一个重要特性。

例如:

 #include <iostream>class Animal {public:virtual void makeSound() {std::cout << "The animal makes a sound" << std::endl;}};​class Dog : public Animal {public:void makeSound() override {std::cout << "The dog barks" << std::endl;}};​int main() {Animal* animal = new Dog();animal->makeSound(); // 输出 "The dog barks"delete animal;return 0;}​

在这个例子中,Animal 类有一个虚函数 makeSound()Dog 类继承了 Animal 类并重写了 makeSound() 函数。当我们通过基类指针调用 makeSound() 时,实际上调用的是派生类 Dog 的实现。这就是虚函数实现多态的一个例子

new和delete的原理

实现过程:

  • new的实现过程是:调用名为operator new的标准库函数,分配足够大的原始类为类型化的内存,以保寸指定类型的一个对象;接下来运行该类型的一个构造函数,用指定初始化构造函数;最后返回指向新分配并构造后的对象指针

  •  new 用于在堆(heap)上分配内存空间以存储一个或多个对象。new 会调用适当的构造函数来初始化对象。new 返回指向分配内存的指针,允许您在程序的其他地方访问该对象。如果分配失败(例如,没有足够的内存可用),new 可能引发 std::bad_alloc 异常,您可以使用 try-catch 块来捕获它。

  • delete的实现过程:对指针指向的对象运行适当的析构函数;然后通过调用名为operator delete的标准库函数释放该对象所用的内存。

  •  delete 用于释放先前由 new 分配的内存。delete 会调用适当的析构函数来销毁对象。delete 接受指向要释放内存的指针,并将该内存返回给系统堆池,以便将其重复利用。如果使用 delete 后未将指针设置为 nullptr(或 NULL),则可能会导致悬空指针(dangling pointer)问题。

new和malloc

1、 new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持;

2、 使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。

3、 new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。

4、 new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。

5、 new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。

C++中有几种类型的new

在C++中,new有三种典型的使用方法:plain new,nothrow new和placement new

image-20231024180017452

vector中push_back和emplace_back的区别

内联函数?内联函数的缺点?

仿函数

关键字

const 关键字

const的作⽤:

被它修饰的值不能改变,是只读变量。必须在定义的时候就给它赋初值。

1、常量指针(底层const

常量指针:

是指定义了⼀个指针,这个指针指向⼀个只读的对象,不能通过常量指针来改变这个对象的值。常量指针强调的是

指针对其所指对象的不可改变性。

特点:

靠近变量名。

形式:

(1)const 数据类型 *指针变量 = 变量名

(2)数据类型 const *指针变量 = 变量名

⽰例:

 int temp = 10;​const int* a = &temp;​int const *a = &temp;​// 更改:​*a = 9; // 错误:只读对象​temp = 9; // 正确

2、指针常量(顶层const

指针常量:

指针常量是指定义了⼀个指针,这个指针的值只能在定义时初始化,其他地⽅不能改变。指针常量强调的是指针的

不可改变性。

特点:

靠近变量类型。

形式:

数据类型 * const 指针变量=变量名

 int temp = 10;int temp1 = 12;int* const p = &temp;// 更改:p = &temp2; // 错误*p = 9; // 正确

image-20231021153829661

definetypedef 的区别

define

  1. 只是简单的字符串替换,没有类型检查

  2. 是在编译的预处理阶段起作⽤

  3. 可以⽤来防⽌头⽂件重复引⽤

  4. 不分配内存,给出的是⽴即数,有多少次使⽤就进⾏多少次替换

typedef

  1. 有对应的数据类型,是要进⾏判断的

  2. 是在编译、运⾏的时候起作⽤

  3. 在静态存储区中分配空间,在程序运⾏过程中内存中只有⼀个拷贝

defineinline 的区别

1define

定义预编译时处理的宏,只是简单的字符串替换,⽆类型检查,不安全。

2inline

int temp = 10;

int temp1 = 12;

int* const p = &temp;

// 更改:

p = &temp2; // 错误

*p = 9; // 正确inline是先将内联函数编译完成⽣成了函数体直接插⼊被调⽤的地⽅,减少了压栈,跳转和返回的操作。没有普

函数调⽤时的额外开销;

内联函数是⼀种特殊的函数,会进⾏类型检查;

对编译器的⼀种请求,编译器有可能拒绝这种请求;

C++inline编译限制:

  1. 不能存在任何形式的循环语句

  2. 不能存在过多的条件判断语句

  3. 函数体不能过于庞⼤

  4. 内联函数声明必须在调⽤语句之前

overrideoverload

1override是重写(覆盖)了⼀个⽅法

以实现不同的功能,⼀般是⽤于⼦类在继承⽗类时,重写⽗类⽅法。

规则:

  1. 重写⽅法的参数列表,返回值,所抛出的异常与被重写⽅法⼀致

  2. 被重写的⽅法不能为private

  3. 静态⽅法不能被重写为⾮静态的⽅法

  4. 重写⽅法的访问修饰符⼀定要⼤于被重写⽅法的访问修饰符(public>protected>default>private)

2overload是重载,这些⽅法的名称相同⽽参数形式不同

⼀个⽅法有不同的版本,存在于⼀个类中。

规则:

  1. 不能通过访问权限、返回类型、抛出的异常进⾏重载

  2. 不同的参数类型可以是不同的参数类型,不同的参数个数,不同的参数顺序(参数类型必须不⼀样)

  3. ⽅法的异常类型和数⽬不会对重载造成影响

使⽤多态是为了避免在⽗类⾥⼤量重载引起代码臃肿且难于维护。

重写与重载的本质区别是,加⼊了override的修饰符的⽅法,此⽅法始终只有⼀个被你使⽤的⽅法。

newmalloc

1、new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL。

2、使⽤new操作符申请内存分配时⽆须指定内存块的⼤⼩,⽽malloc则需要显式地指出所需内存的尺⼨。

3、opeartor new /operator delete可以被重载,⽽malloc/free并不允许重载。

4、new/delete会调⽤对象的构造函数/析构函数以完成对象的构造/析构。⽽malloc则不会

5、malloc与free是C++/C语⾔的标准库函数,new/delete是C++的运算符

6、new操作符从⾃由存储区上为对象动态分配内存空间,⽽malloc函数从堆上动态分配内存。

constexprconst

const 表⽰“只读”的语义,constexpr 表⽰“常量”的语义

constexpr 只能定义编译期常量,⽽ const 可以定义编译期常量,也可以定义运⾏期常量。

你将⼀个成员函数标记为constexpr,则顺带也将它标记为了const。如果你将⼀个变量标记为constexpr,则同样它

是const的。但相反并不成⽴,⼀个const的变量或函数,并不是constexpr的。

static

作⽤:实现多个对象之间的数据共享 + 隐藏,并且使⽤静态成员还不会破坏隐藏原则;默认初始化为0

前置++与后置++

 self &operator++() {node = (linktype)((node).next);return *this;}const self operator++(int) {self tmp = *this;++*this;return tmp;}

为了区分前后置,重载函数是以参数类型来区分,在调⽤的时候,编译器默默给int指定为⼀个0

1、为什么后置返回对象,⽽不是引⽤

因为后置为了返回旧值创建了⼀个临时对象,在函数结束的时候这个对象就会被销毁,如果返回引⽤,那么我请问

你?你的对象对象都被销毁了,你引⽤啥呢?

2、为什么后置前⾯也要加const

其实也可以不加,但是为了防⽌你使⽤i++++,连续两次的调⽤后置++重载符,为什么呢?

原因:

它与内置类型⾏为不⼀致;你⽆法活得你所期望的结果,因为第⼀次返回的是旧值,⽽不是原对象,你调⽤两次后

置++,结果只累加了⼀次,所以我们必须⼿动禁⽌其合法化,就要在前⾯加上const。

3、处理⽤户的⾃定义类型

最好使⽤前置++,因为他不会创建临时对象,进⽽不会带来构造和析构⽽造成的格外开销。

c和c++中const的区别是什么

  • const的编译方式不同,c中,const就是当作一个变量来编译生成指令的.

  • c++中,所有出现const常量名字的地方,都被常量的初始化替换了! !!


bind的用法

我们通常是在 C++ 中进行函数绑定的上下文中。std::bind 是一个非常有用的函数模板,它允许你创建一个可调用对象(函数、函数指针、成员函数等),并绑定一些参数或者改变调用时的顺序。这对于在一些场景中很方便,特别是在涉及回调、函数对象、线程等情况下。

以下是一个简单的例子来说明 std::bind 绑定全局函数的基本用法:

 #includ<iostream>#include<function>//一个简单的函数void greet(consr std::string& name,const std::string& greeting){std::cont<<greeting<<", "<<name<<"!"<<std::endl;}int main(){//使用bind绑定器绑定greet函数的第一个参数auto greetSomeone = std::bind(greet, std::placeholders::_1,"hello");//调用可以调用的对象greetSomeone("Alice");//输出 hello,Alice!greetSomeone("Bob");//输出hello,Bob!}​

在这个例子中,std::bind 用于创建一个可调用对象 greetSomeone,它是 greet 函数的一个变体,其中第一个参数(name)被绑定为 std::placeholders::_1,而第二个参数(greeting)被指定为常量字符串 "Hello"。

bind绑定非静态成员函数

 #include <functional>#include <iostream>​class MyClass {public:void printMessage(const std::string& message) {std::cout << "Message: " << message << std::endl;}};​int main() {MyClass obj;​// 使用 std::bind 绑定非静态成员函数auto func = std::bind(&MyClass::printMessage, &obj, std::placeholders::_1);​// 调用包装的成员函数func("Hello, world!");  // 输出:Message: Hello, world!​return 0;}​

在这个例子中,使用 std::bind 绑定了 MyClass 中的非静态成员函数 printMessage。需要注意的是,&obj 作为额外的参数传递给 std::bind,代表了 this 指针。

绑定成员函数和对象

如果你希望在绑定时不显式传递对象,而是在调用时传递,可以使用 std::placeholders::_1 占位符占位。

 #include <functional>#include <iostream>​class MyClass {public:void printMessage(const std::string& message) {std::cout << "Message: " << message << std::endl;}};​int main() {MyClass obj;​// 使用 std::bind 绑定成员函数和对象auto func = std::bind(&MyClass::printMessage, std::placeholders::_1);​// 调用包装的成员函数,传递对象作为参数func(obj, "Hello, world!");  // 输出:Message: Hello, world!​return 0;}

在这个例子中,std::placeholders::_1 占位符占位了对象参数,而在调用时通过 func(obj, "Hello, world!") 传递了实际的对象。

一些关键概念:

  • std::placeholders::_1:这是一个占位符,表示绑定时应该被替代的第一个参数。在这里,它代表 greetSomeone 被调用时传递的参数。

  • greetSomeone 是一个可调用对象,它被绑定到 greet 函数,并预设了一个参数("Hello"),所以当你调用 greetSomeone("Alice") 时,实际上就是调用了 greet("Alice", "Hello")

function的用法

std::function 提供了一种通用的、类型安全的方式来包装和存储可调用对象。以下是 std::function 的一些详细用法:

基本用法

 #include <iostream>#include <functional>​int add(int a, int b) {return a + b;}​int main() {// 使用 std::function 包装函数std::function<int(int, int)> func = add;​// 调用包装的函数int result = func(3, 4);std::cout << "Result: " << result << std::endl;  // 输出:Result: 7​return 0;}​

Lambda 表达式

 #include <iostream>#include <functional>​int main() {// 使用 std::function 包装 Lambda 表达式std::function<int(int, int)> func = [](int a, int b) {return a * b;};​// 调用包装的 Lambda 表达式int result = func(3, 4);std::cout << "Result: " << result << std::endl;  // 输出:Result: 12​return 0;}​

函数对象

 #include <iostream>#include <functional>​// 函数对象struct Multiply {int operator()(int a, int b) const {return a * b;}};​int main() {// 使用 std::function 包装函数对象std::function<int(int, int)> func = Multiply();​// 调用包装的函数对象int result = func(3, 4);std::cout << "Result: " << result << std::endl;  // 输出:Result: 12​return 0;}​

多态性

 #include <iostream>#include <functional>​void printMessage(const std::string& message) {std::cout << message << std::endl;}​void printNumber(int number) {std::cout << "Number: " << number << std::endl;}​int main() {// 使用 std::function 容纳不同签名的函数std::function<void(const std::string&)> func1 = printMessage;std::function<void(int)> func2 = printNumber;​// 调用包装的函数func1("Hello, world!");  // 输出:Hello, world!func2(42);               // 输出:Number: 42​return 0;}​

绑定参数

 #include <iostream>#include <functional>​int add(int a, int b) {return a + b;}​int main() {// 使用 std::bind 绑定参数std::function<int(int)> add5 = std::bind(add, std::placeholders::_1, 5);​// 调用包装的函数int result = add5(10);std::cout << "Result: " << result << std::endl;  // 输出:Result: 15​return 0;}​

这些示例涵盖了 std::function 的基本用法,包括包装函数、Lambda 表达式、函数对象、多态性、绑定参数以及清空或重置等方面。希望这能够帮助你更好地理解和使用 std::function

Protocol Buffers(protobuf)是一种由Google开发的轻量级的数据交换格式,它可以用于结构化数据的序列化,类似于 XML 或 JSON,但更轻量、更高效。Protocol Buffers 不仅用于数据的序列化和反序列化,还可以定义数据结构和接口。

以下是 Protocol Buffers 序列化的基本原理和用法:

Protocol Buffers 的定义

Protocol Buffers 使用 .proto 文件来定义数据结构和消息格式。一个简单的 .proto 文件如下:

 protoCopy codesyntax = "proto3";​message Person {required string name = 1;required int32 id = 2;optional string email = 3;}

在这个例子中,我们定义了一个名为 Person 的消息,其中包含了 nameidemail 三个字段。每个字段都有一个唯一的标识号,用于在二进制序列中标识字段。

使用 Protocol Buffers 编译器

.proto 文件编译成对应的编程语言的类文件,可以使用 Protocol Buffers 编译器。例如,使用 protoc 编译器可以将上述 .proto 文件编译成 C++ 代码:

 bashCopy codeprotoc --cpp_out=. your_proto_file.proto

序列化和反序列化

在编程语言中,你可以使用生成的 Protocol Buffers 类来序列化和反序列化数据。以下是一个简单的 C++ 示例:

 cppCopy code#include "your_proto_file.pb.h"#include <iostream>#include <fstream>​int main() {// 创建一个 Person 对象Person person;person.set_name("John Doe");person.set_id(123);person.set_email("john.doe@example.com");​// 序列化std::string serializedData;person.SerializeToString(&serializedData);​// 反序列化Person deserializedPerson;deserializedPerson.ParseFromString(serializedData);​// 输出反序列化后的数据std::cout << "Name: " << deserializedPerson.name() << std::endl;std::cout << "ID: " << deserializedPerson.id() << std::endl;std::cout << "Email: " << deserializedPerson.email() << std::endl;​return 0;}

在这个例子中,我们使用 SerializeToStringPerson 对象序列化为字符串,然后使用 ParseFromString 将字符串反序列化为另一个 Person 对象。序列化后的数据可以存储在文件中、通过网络传输等方式。

4. Protocol Buffers 的优势

  • 高效性: Protocol Buffers 使用二进制格式,相比 XML 和 JSON 更加紧凑,占用更少的空间。

  • 速度快: 序列化和反序列化的速度比 XML 和 JSON 更快。

  • 可扩展: 可以在不破坏现有数据的情况下轻松添加或删除字段,使其具有很好的向前和向后兼容性。

总体而言,Protocol Buffers 是一种强大且高效的数据序列化工具,特别适用于需要高性能和小数据大小的场景,如分布式系统通信、数据存储和通信协议定义。

序列化

序列化是将数据结构或对象转换为字节流(二进制数据)或其他格式的过程,以便将其存储在文件中、通过网络发送,或在不同系统之间传输。序列化后的数据通常可以被反序列化还原为原始的数据结构或对象。

为什么需要序列化

  1. 数据持久化: 将数据保存在文件中,以便在程序关闭后重新加载。

  2. 数据传输: 在网络上传输数据,例如客户端和服务器之间的通信。

  3. 进程间通信: 在不同的进程之间共享数据。

json序列化

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,广泛用于前后端之间的数据传输。JSON 序列化即将数据结构或对象转换为 JSON 格式的字符串,而 JSON 反序列化则是将 JSON 格式的字符串还原为原始的数据结构或对象。

Lambda表达式

Lambda 表达式是 C++11 引入的一项特性,它允许在代码中以一种更为简洁和方便的方式定义匿名函数。Lambda 表达式的本质可以从以下几个方面来理解:

  1. 匿名函数: Lambda 表达式实际上是一个匿名函数,即没有显式名称的函数。它允许你在需要函数的地方直接定义函数,而不必显式地写出函数名。

  2. 语法糖: Lambda 表达式是 C++ 对函数对象(function object)的一种语法糖,使得使用函数对象更为简洁。在使用 Lambda 表达式之前,我们通常需要使用函数对象或函数指针来传递函数,Lambda 表达式使这一过程变得更加直观。

  3. 闭包: Lambda 表达式可以捕获其定义位置的变量,形成一个闭包(closure)。通过捕获变量,Lambda 表达式可以在其生命周期内访问这些变量,即使这些变量在定义 Lambda 表达式的作用域之外。

  4.  #icnlude<iostream>int main(){//lambda表达式没有捕获变量auto add = [](int a,int b){return a + b;};//使用lamabda表达式int result = add(2,3);cuot<<"result:"<<result<<endl;return 0;}

    在这个例子中,auto add = [](int a, int b) { return a + b; }; 定义了一个 Lambda 表达式,它接受两个整数参数并返回它们的和。Lambda 表达式使用 auto 关键字,因为编译器能够推断出 Lambda 表达式的类型。

    Lambda 表达式的本质是一种语法糖,它提供了更简洁的语法来定义函数,并允许在函数内部捕获外部变量,使得代码更为灵活和直观。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/224054.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【状态机FSM 序列检测 饮料机_2023.12.1】

同步状态机 概念 同步状态机&#xff08;同一脉冲边沿触发&#xff09;&#xff1a;有限个离散状态及某状之间的转移 异步状态机无法综合 分类 Moore状态机 只和状态有关&#xff0c;与输入无关 Mealy状态机 和状态和输入都有关 Mealy型比Moore型少一个状态 结构 由状态寄…

Tomcat从认识安装到详细使用

文章目录 一.什么是Tomact?二.Tomcat的安装1.下载安装包2.一键下载3.打开Tomcat进行测试4.解决Tomcat中文服务器乱码 三.Tomcat基本使用1.启动与关闭Tomcat2.Tomcat部署项目与浏览器访问项目 四.Tomcat操作中的常见问题1.启动Tomcat后&#xff0c;启动窗口一闪而过&#xff1f…

排序 | 冒泡插入希尔选择堆快排归并计数排序

排序 | 冒泡插入希尔选择堆快排归并计数排序 文章目录 排序 | 冒泡插入希尔选择堆快排归并计数排序冒泡排序插入排序希尔排序选择排序堆排序快速排序--交换排序三数取中快速排序hoare版本快速排序挖坑法快速排序前后指针法 快速排序--非递归实现归并排序归并排序非递归实现非比…

【AutoDL】使用云服务器跑深度学习代码

一、AutoDL租用服务器 1.选用服务器 1.算力市场 租用服务器&#xff0c;选择自己心仪的服务器 2.镜像 可以选择一些基础的镜像&#xff0c;社区镜像里是git上有的环境。 3.上传文件 在文件存储中&#xff0c;选择上传的区&#xff0c;在右边点击上传&#xff0c;选择自己的文…

Dockerfile创建镜像LNMP+WordPress

目录 实验部署 nginx 配置mysql 配置php 实验部署 INMPwordpress nginx 172.111.0.10 docker-nginx mysql 172.111.0.20 docker-mysql php 172.111.0.30 docker-php nginx 关闭防火墙和安全机制在opt目录创建nginx MySQL php目录 cd nginx mysql php vim Dockerfile#声…

Arduino中读取SD卡文本文件数据

1、硬件 2、代码 #include <SD.h> #include <SPI.h> // needed for Arduino versions later than 0018const long _1000msTime 1000; // 1000 milli seconds unsigned long _1000msLastTime;File myFile; int date; int Lastdate; String strDate;//…

YOLOv5独家原创改进:SPPF自研创新 | SPPF与感知大内核卷积UniRepLK结合,大kernel+非膨胀卷积提升感受野

💡💡💡本文自研创新改进:SPPF与感知大内核卷积UniRepLK结合,大kernel+非膨胀卷积,使SPPF增加大kernel,提升感受野,最终提升检测精度 收录 YOLOv5原创自研 https://blog.csdn.net/m0_63774211/category_12511931.html 💡💡💡全网独家首发创新(原创),…

垃圾回收 (GC) 在 .NET Core 中是如何工作的?(二)

接上一篇文章垃圾回收 (GC) 在 .NET Core 中是如何工作的&#xff1f;-CSDN博客 GC 会分配堆段&#xff0c;其中每个段都是一系列连续的内存。 置于堆中的对象归类为 3 个代系之一&#xff1a;0、1 或 2。 代系可确定 GC 尝试在应用不再引用的托管对象上释放内存的频率。 编号…

【亚马逊云科技】使用Vscode Amazon-Q完成GUI界面粉笔脚本开发

本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 亚马逊云科技开发者社区, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 前言 亚马逊云科技-Q&#xff0c;可以快速获得紧迫问题的相关答案&#xff0c;解决问题…

生物信息学分析领域领先的特制语言环境NGLess(Next Generation Less)介绍、安装配置和详细使用方法

介绍 NGLess&#xff08;Next Generation Less&#xff09;是一种用于生物信息学分析的领先的领域特定语言&#xff08;DSL&#xff09;。它旨在简化和加速NGS&#xff08;Next Generation Sequencing&#xff09;数据的分析过程。NGLess具有清晰的语法和功能&#xff0c;使用户…

机器翻译:跨越语言边界的智能大使

导言 机器翻译作为人工智能领域的瑰宝&#xff0c;正在以前所未有的速度和精度&#xff0c;为全球沟通拓展新的可能性。本文将深入研究机器翻译的技术原理、应用场景以及对语言交流未来的影响。 1. 简介 机器翻译是一项致力于通过计算机自动将一种语言的文本翻译成另一种语言的…

[Unity]关于Unity接入Appsflyer并且打点支付

首先需要去官方下载Appsflyer的UnityPackage 链接在这afPackage 然后导入 导入完成 引入此段代码 using AppsFlyerSDK; using System.Collections; using System.Collections.Generic; using UnityEngine;public class AppflysManager : MonoBehaviour {public static App…

测试总监给我分享的《接口自动化测试》总结,让我成功的入门接口自动化门槛......

前两天在测试技术交流群里&#xff0c;听了一位字节跳动的测试总监分享的接口自动化测试的内容&#xff0c;对接口自动化更加了解了&#xff0c;也为自己接下来在公司实施接口自动化项目提供了思路。 前言 自动化测试&#xff0c;算是近几年比较火热的一个话题&#xff0c;当…

现代C++ 实现单例模式

传统写法有什么问题 如果你了解过单例模式&#xff0c;双重检查锁定模式&#xff08;Double-Checked Locking Pattern&#xff0c;后文简称DCLP&#xff09;的写法你一定不会陌生&#xff0c;甚至你或许认为它是最正确的代码。 class Singleton { public://获取单例Singleton…

解决:Component name “index“ should always be multi-word

原因 要求组件名称以驼峰格式命名&#xff0c;自定义组件名称应该由多单纯组成&#xff0c;防止和html标签冲突&#xff0c;所以index.vue 会报错 解决 1、按照规则驼峰格式&#xff0c;如&#xff1a;appIndex.vue 2、若有.eslintrc.js文件&#xff0c;并在规则中(rules)关…

数据挖掘-08-基于Python实现时间序列分析建模(ARIMA 模型)(包括数据和代码)

文章目录 0. 数据代码下载1. 背景描述2. 预测目的3. 数据总览4. 数据预处理4.1数据描述性统计与清洗a. 导入程序库b. 读取数据c. 查看统计信息和空值d. 查看是否有重复数据以及清理重复数据e. 空值清理f. 针对清洗后的数据进行统计分析 5. 探索性数据分析5.1 数据分析 6. 构建 …

猫粮哪个牌子质量好性价比高?盘点十款主食冻干猫粮品牌排行榜!

在过去的100多年里&#xff0c;猫咪主食市场一直被膨化猫粮主导。然而&#xff0c;随着猫咪频频出现猝死、失明、发育不良以及营养不良等问题&#xff0c;猫主人们开始质疑膨化粮是否最适合猫咪。于是&#xff0c;从上世纪90年代开始&#xff0c;出现了生骨肉喂养。生骨肉确实是…

网络安全——基于Snort的入侵检测实验

一、实验目的要求&#xff1a; 二、实验设备与环境&#xff1a; 三、实验原理&#xff1a; 四、实验步骤&#xff1a; 五、实验现象、结果记录及整理&#xff1a; 六、分析讨论与思考题解答&#xff1a; 七、实验截图&#xff1a; 一、实验目的要求&#xff1a; 1、掌握…

一、win10+yolov8+anaconda环境部署

1、安装anaconda &#xff08;1&#xff09;打开aonconda下载地址&#xff1a;https://www.anaconda.com/download&#xff0c;点击download下载。 2、下载完成后&#xff0c;双击打开&#xff0c;点击Next&#xff0c;I Agree&#xff0c;选择just me&#xff1b; 3、勾选…

Spring上下文之注解模块ConfigurationMethod

博主介绍:✌全网粉丝5W+,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验✌ 博主作品:《Java项目案例》主要基于SpringBoot+MyBatis/MyBatis-plus+…