C++题目
1、内存管理
1.内存模型
栈:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。
堆:就是那些由new分配的内存块,其释放由程序员控制(一个new对应一个delete)。
自由存储区:堆是操作系统维护的一块内存,自由存储区是C++中通过new和delete动态分配和释放对象的抽象概念,和堆比较像,但不等价。
常量存储区:存储常量,不允许修改。
代码区:存放函数体的二进制代码。
2.内存泄漏
内存泄漏是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况;比如,new申请资源后没有delete,子类继承父类时,父类析构函数不是虚函数。
检测:一些常见的工具插件,如ccmalloc、Dmalloc、Leaky、Valgrind等等。
解决办法:智能指针
3.内存对齐
什么是内存对齐?
在C语言中,结构体是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构体、联合体等)的数据单元。在结构体中,编译器为结构体的每个成员按其自然边界(alignment)分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构体的地址相同。
为了使CPU能够对变量进行快速的访问,变量的起始地址应该具有某些特性,即所谓的“对齐”,比如4字节的int型,其起始地址应该位于4字节的边界上,即起始地址能够被4整除,也即“对齐”跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。
为什么要内存对齐?
需要字节对齐的根本原因在于CPU访问数据的效率问题。假设上面整型变量的地址不是自然对齐,比如为0x00000002,则CPU如果取它的值的话需要访问两次内存,第一次取从0x00000002-0x00000003的一个short,第二次取从0x00000004-0x00000005的一个short然后组合得到所要的数据,如果变量在0x00000003地址上的话则要访问三次内存,第一次为char,第二次为short,第三次为char,然后组合得到整型数据。
而如果变量在自然对齐位置上,则只要一次就可以取出数据。一些系统对对齐要求非常严格,比如sparc系统,如果取未对齐的数据会发生错误,而在x86上就不会出现错误,只是效率下降。
2、C++基础语法
1.在main执行之前和之后执行的代码
main函数执行之前,主要就是初始化系统相关资源:
-
设置栈指针
-
初始化静态static变量和global全局变量,即.data段的内容
-
将未初始化部分的全局变量初始化,short、int、long等为0,bool为false,指针为NULL等
-
全局对象初始化,在main之前调用构造函数,这是可能会执行前的一些代码
-
将main函数的参数argc、argv等传递给main函数,然后允许main函数
main函数执行之后:
-
全局对象的析构函数执行
-
可以使用atexit注册一个函数,它会在main之后执行
2.简述C++从代码到可执行二进制文件的过程
一个C++程序从源代码到可执行程序有四个过程:预处理,编译,汇编,链接。
-
预编译:这个过程主要的处理操作如下:
(1) 将所有的#define删除,并且展开所有的宏定义
(2) 处理所有的条件预编译指令,如#if、#ifdef
(3) 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。
(4) 过滤所有的注释
(5) 添加行号和文件名标识。
-
编译:这个过程主要的处理操作如下:
(1) 词法分析:将源代码的字符序列分割成一系列的记号。
(2) 语法分析:对记号进行语法分析,产生语法树。
(3) 语义分析:判断表达式是否有意义。
(4) 代码优化:
(5) 目标代码生成:生成汇编代码。
(6) 目标代码优化:
-
汇编:这个过程主要是将汇编代码转变成机器可以执行的指令。
-
链接:将不同的源文件产生的目标文件进行链接,从而形成一个可以执行的程序。链接分为静态链接和动态链接。
静态链接:是在链接的时候就已经把要调用的函数或者过程链接到了生成的可执行文件中,就算你在去把静态库删除也不会影响可执行程序的执行;生成的静态链接库,Windows下以.lib为后缀,Linux下以.a为后缀。每当库函数的代码修改了,都需要重新进行编译链接形成可执行程序。但是运行速度快。
动态链接:是在链接的时候没有把调用的函数代码链接进去,而是在执行的过程中,再去找要链接的函数,生成的可执行文件中没有函数代码,只包含函数的重定位信息,所以当你删除动态库时,可执行程序就不能运行。生成的动态链接库,Windows下以.dll为后缀,Linux下以.so为后缀。更新方便,但是每次都需要进行链接,性能会有一定损耗。
3.引用和指针的区别
指针:指针相当于一个变量,但是他和不同变量不一样,它存放的是其他变量在内存中的地址。指针名指向了内存的首地址。在32位平台下,无论指针类型是什么,sizeof p = 4,在64位平台下,sizeof p 都是8。指针在初始化后可以改变指向。
引用:引用是原来的变量实质上是一个东西,是原来变量的别名,引用只能有一级,引用不能为NULL而且在定义时必须初始化,在初始化之后不可再改变,sizeof引用得到的是引用所指向变量的大小。
在汇编层面,一些编译器把引用当成指针操作,因此引用也会占用空间,是否占用空间,应该结合编译器分析。
当把指针作为参数进行传递时,也是将实参的一个拷贝传递给形参,两者指向的地址相同,但不是同一个变量,在函数中改变这个变量的指向不影响实参,而引用却可以。
在传递函数参数时,什么时候用指针,什么时候用引用?
-
需要返回函数局部变量的内存时候用指针,使用指针传参需要开辟内存,用完要记得释放指针内存,不然会内存泄漏,而返回局部变量的引用是没有意义的。
-
对栈空间大小敏感的时候(比如递归)使用引用。使用引用传递不需要创建临时变量,开销要更小。
-
类对象作为参数传递时使用引用,这是C++类对象传递的标准方式。
4.指针数组、数组指针、函数声明和函数指针
int *p[10]; // 指针数组,数组内有10个元素,每个元素都是指向int类型的指针int (*p)[10]; // 数组指针,是一个指针,指向一个int类型的数组int*p(int a); // 函数声明,返回值是int*类型的,参数是int类型int (*p)(int a); // 函数指针,指向一个返回值为int,参数为int的函数
5.说说new和malloc的区别,以及各自底层实现原理
-
new是操作符,而malloc是函数。
-
new在调用的时候先分配内存,在调用构造函数,释放的时候调用析构函数;而malloc没有构造函数和析构函数。
-
malloc需要给定申请内存的大小,返回的指针需要强转;new会调用构造函数,不用指定内存的大小,返回指针不用强转。
-
new可以被重载;malloc不行
-
new分配内存更直接和安全。
-
new发生错误抛出异常,malloc返回null。
malloc底层实现:当开辟的空间小于 128K 时,调用 brk()函数;当开辟的空间大于 128K 时,调用mmap()。malloc采用的是内存池的管理方式,以减少内存碎片。先申请大块内存作为堆区,然后将堆区分为多个内存块。当用户申请内存时,直接从堆区分配一块合适的空闲快。采用隐式链表将所有空闲块,每一个空闲块记录了一个未分配的、连续的内存地址。
new底层实现:关键字new在调用构造函数的时候实际上进行了如下的几个步骤:
-
首先调用operator new的标准库函数,分配足够大的原始为类型化的内存,创建一个新的对象
-
将构造函数的作用域赋值给这个新的对象(因此this指向了这个新的对象)
-
执行构造函数中的代码(为这个新对象添加属性)
-
返回新对象
6.说说内联函数和宏函数的区别
-
宏定义不是函数,但是使用起来像函数。预处理器用复制宏代码的方式代替函数的调用,省去了函数压栈退栈过程,提高了效率;而内联函数本质上是一个函数,内联函数一般用于函数体的代码比较简单的函数,不能包含复杂的控制语句,while、switch,并且内联函数本身不能直接调用自身。
-
宏函数是在预编译的时候把所有的宏名用宏体来替换,简单的说就是字符串替换 ;而内联函数则是在编译的时候进行代码插入,编译器会在每处调用内联函数的地方直接把内联函数的内容展开,这样可以省去函数的调用的开销,提高效率
-
宏定义是没有类型检查的,无论对还是错都是直接替换;而内联函数在编译的时候会进行类型的检查,内联函数满足函数的性质,比如有返回值、参数列表等
inline函数一般用于比较小的,频繁调用的函数,最好定义在头文件,而不仅仅是声明,因为编译器在处理inline函数时,需要在调用点内联展开该函数,所以仅需要声明函数是不够的。
7.说说const和define的区别
const用于定义常量;而define用于定义宏,而宏也可以用于定义常量。都用于常量定义时,它们的区别有:
-
const生效于编译的阶段;define生效于预处理阶段。
-
const定义的常量,在C语言中是存储在内存中、需要额外的内存空间的;define定义的常量,运行时是直接的操作数,并不会存放在内存中。
-
const定义的常量是带类型的;define定义的常量不带类型。因此define定义的常量不利于类型检查。
8.C++中const和static关键字的作用
static
-
定义全局静态变量和局部静态变量:在变量前面加上static关键字。初始化的静态变量会在数据段分配内存,未初始化的静态变量会在BSS段分配内存。直到程序结束,静态变量始终会维持前值。只不过全局静态变量和局部静态变量的作用域不一样;
-
定义静态函数:在函数返回类型前加上static关键字,函数即被定义为静态函数。静态函数只能在本源文件中使用;
-
在变量类型前加上static关键字,变量即被定义为静态变量。静态变量只能在本源文件中使用;
-
在c++中,static关键字可以用于定义类中的静态成员变量:使用静态数据成员,它既可以被当成全局变量那样去存储,但又被隐藏在类的内部。类中的static静态数据成员拥有一块单独的存储区,而不管创建了多少个该类的对象。所有这些对象的静态数据成员都共享这一块静态存储空间。
-
在c++中,static关键字可以用于定义类中的静态成员函数:与静态成员变量类似,类里面同样可以定义静态成员函数。只需要在函数前加上关键字static即可。如静态成员函数也是类的一部分,而不是对象的一部分。所有这些对象的静态数据成员都共享这一块静态存储空间,不具有this指针。静态成员函数在类内定义,必须在类外初始化。不能访问类对象的非static成员变量和非static成员函数。
const
-
cosnt常量在定义时必须初始化,之后无法更改;
-
const形参可以接受const和非const类型的实参;
-
const成员变量:不能在类定义外部初始化,只能通过构造函数初始化列表进行初始化,并且必须有构造函数,不同类对其const数据成员可以不同,所以不能再类中声明初始化。
-
const成员函数:const对象不可以调用非const成员函数,非const对象都可以调用;不可以改变非mutable数据的值。
9.final和override关键字
override
当在父类中使用了虚函数时候,可能需要在某个子类中对这个虚函数进行重写:如果不使用override将虚函数的名字写错了,这时候override的作用就出来了,它指定了子类的这个虚函数是重写的父类的,如果名字错误的话,编译器是不会通过的。
class A{virtual void foo();};class B : public A{virtual void f00(); // 新增的函数virtual void foo() override;};
final
当不希望某个类被继承时,或不希望某个虚函数被重写时,可以在类名和虚函数后面添加final关键字,添加final关键字后被继承或重写时,编译器会报错。
10.volatile、mutable和explicit关键字
1、volatile
volotile关键字是一种类型修饰符,用它声明的类型变量表示可能被某些编译器未知的因素修改。因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。多线程应用中被几个任务共享的变量应该加 volatile;
2、mutable
mutable是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰。
3、explicit
它用来修饰只有一个参数的类构造函数,以表明该构造函数是显式的,而非隐式的。当使用explicit修饰构造函数时,它将禁止类对象之间的隐式转换,以及禁止隐式调用拷贝构造函数。
class Foo {public:Foo(int x) { /* ... */ }};void bar(Foo f) {// ...}int main() {bar(42); // 隐式转换,调用Foo(42)}// 在这里,`bar`函数需要一个`Foo`对象,但传入了一个`int`,编译器会隐式调用`Foo(int)`构造函数。如果这不符合预期,加上`explicit`class Foo {public:explicit Foo(int x) { /* ... */ }};void bar(Foo f) {// ...}int main() {bar(42); // 错误:不能隐式转换int到Foobar(Foo(42)); // 正确:显式转换}
11.说说什么是野指针和悬空指针,怎么产生的,如何避免?
-
概念:野指针就是没有被初始化的指针,悬空指针就是最初指向的内存已经被释放的一种指针。
-
产生原因:释放内存后指针不及时置空(野指针),依然指向了该内存,那么可能出现非法访问的错误。这些我们都要注意避免。
-
避免办法:
(1)初始化置NULL
(2)申请内存后判空
(3)指针释放后置NULL
(4)使用智能指针
12.C++的顶层const和底层const
-
顶层const:指的是const修饰的变量本身是一个常量,无法修改。
-
底层const:指的是const修饰的变量所指向的对象是一个常量,指的是所知变量。
int i = 10;const int ii = 11; // 顶层const: 对象 ii 本身是常量const int *a = &i; // 底层const: 指针对象 a 本身不是常量,而所存地址指向的对象是常量int const *b = &i; // 底层const: 与上一条语句写法不同,但都是表示指向常量的指针,即指针常量int* const c = &i; // 顶层const: 指针对象 c 本身是常量,即常量指针const int* const d = &i; // 左侧const是底层const,右侧const是顶层constconst int& e = i; // 底层const: 引用所存地址指向的对象是常量
-
常量指针:即指针本身是常量,表示指针存储的地址不可被修改
-
指针常量:即指向常量的指针,表示其所指向的对象不可被修改
int i = 10;int* const a = &i; //顶层 const => 常量指针const int* b = &i; //底层 const => 指针常量const int* const c = &i; //指向常量对象的常量指针,既有底层 const 又有 顶层 const
13.如何用代码判断大小端存储?
大端存储:字数据的高字节存储在低地址中。
小端存储:字数据的低字节存储在低地址中。
在socket编程中,往往需要将操作系统所用的小端存储的IP地址转换为大端存储,这样才能进行网络传输。
#include <iostream>using namespace std;int main(){int a = 0x1234;//由于int和char的长度不同,借助int型转换成char型,只会留下低地址的部分char c = (char)(a);if (c == 0x12)cout << "big endian" << endl;else if(c == 0x34)cout << "little endian" << endl;}
14.malloc、realloc、calloc的区别
void *malloc(unsigned int num_size);int *p = malloc(20*sizeof(int));// 省去了人为空间计算,malloc申请的空间的值是随机初始化的,calloc申请的空间的值是初始化为0的void *calloc(size_t n, size_t size);int *p = calloc(20, sizeof(int));// 给动态分配的空间分配额外的空间,用于扩充容量void realloc(void *p, size_t new_size);
15.C++函数调用的压栈过程
当函数从入口函数main函数开始执行时,编译器会将我们操作系统的运行状态,main函数的返回地址、main的参数,main函数中的变量、进行依次压栈。当main函数开始调用func函数时,编译器会将main函数的运行状态进行压栈,再将func函数的返回地址,func函数的参数从右到左,func定义变量依次压栈;当func函数调用f函数时,编译器此时会将func函数的运行状态进行压栈,再将返回地址,f函数的参数从右到左,f函数定义变量依次压栈,先执行f后,在执行func最后执行main。
16.全局变量和局部变量有什么区别?
生命周期不同:全局变量随主程序创建而创建,随主程序销毁而销毁;局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在。
使用方式不同:通过声明后全局变量在程序的各个部分都可以用到;局部变量分配在堆栈区,只能在局部使用。
操作系统和编译器通过内存分配的未知可以区分两者:全局变量分配在全局数据端并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面。
17.C++中struct和class的区别
相同点:
-
两者都拥有成员函数、公有和私有部分
-
任何可以使用class完成的工作,同样可以使用struct完成
不同点
-
两者中如果不对成员指定公私有,struct默认是公有的,class默认是私有的
-
class默认private继承,而struct默认是public继承
3、面向对象
1.面向对象的三大特性
面向对象的三大特性是封装、继承、多态。
-
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互。
-
多态:用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。实现多态,有二种方式,重写,重载。
-
继承:可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。三种继承方式:
继承方式 | private继承 | protected继承 | public继承 |
---|---|---|---|
基类的private成员 | 不可见 | 不可见 | 不可见 |
基类的protected成员 | 变为private成员 | 仍为protected成员 | 仍为protected成员 |
基类的public成员 | 变为private成员 | 变为protected成员 | 仍为public成员仍为public成员 |
2.C++中的重载、重写的区别
重载:重载是指在同一范围定义中的同名成员函数才存在重载函数。主要特点是函数名相同,参数类型和数目所不同,不能出现参数个数和类型均相同,仅仅依靠返回值不同来区分的函数。重载和函数成员是否是虚函数无关。
重写:重写是指在派生类中覆盖基类中的同名函数,重写就是重写函数体,要求基类函数必须是虚函数,运行时根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。
-
用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数。
-
存在虚函数的类都有一个一维的虚函数表叫做虚表,类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的。
-
多态性是一个接口多种实现,是面向对象的核心,分为类的多态性和函数的多态性。
-
重写用虚函数来实现,结合动态绑定。
-
纯虚函数是虚函数再加上 = 0。
-
抽象类是指包括至少一个纯虚函数的类。
纯虚函数:virtual void fun()=0。即抽象类必须在子类实现这个函数,即先有名称,没有内容,在派生类实现内容。
重载与重写的区别:
-
重写是父类和子类之间的垂直关系,重载是不同函数之间的水平关系
-
重写要求参数列表相同,重载则要求参数列表不同,返回值不要求
-
重写关系中,调用方法根据对象类决定,重载根据调用时实参表与形参表的对应关系来选择函数体。
3.说说C++中构造函数有几种,分别有什么作用?
C++中的构造函数可以分为四类:默认构造函数,初始化构造函数,拷贝构造函数,移动构造函数。
1、默认构造函数和初始化构造函数。在定义类的对象的时候,完成对象的初始化工作。
class Student{public://默认构造函数Student(){num=1001;age=18; }//初始化构造函数Student(int n,int a):num(n),age(a){}private:int num;int age;};int main(){//用默认构造函数初始化对象S1Student s1;//用初始化构造函数初始化对象S2Student s2(1002,18);return 0;}
有了有参数的构造函数,编译器就不提供默认的构造函数了。
2、拷贝构造函数
#include "stdafx.h"#include "iostream.h"class Test{int i;int *p;public:Test(int ai,int value){i = ai;p = new int(value);}~Test(){delete p;}Test(const Test& t){this->i = t.i;this->p = new int(*t.p);}};//复制构造函数用于复制本类的对象int main(int argc, char* argv[]){Test t1(1,2);Test t2(t1);//将对象t1复制给t2。注意复制和赋值的概念不同return 0;}
赋值构造函数默认实现的是值拷贝(浅拷贝)3、移动构造函数用于将其他类型的变量,隐式转换为本类对象。下面的代码,将int的r转换为Student类型的对象,对象的age为r,num = 1004Student(int r){int num=1004;int age= r;}
4.浅拷贝和深拷贝的区别
浅拷贝
浅拷贝只是拷贝一个指针,并没有新开辟一个地址,拷贝的指针和原来的指针指向同一块地址,如果原来的指针指向的资源释放了,那么再释放浅拷贝的指针的资源就会出现错误。
深拷贝
深拷贝不仅拷贝值,还开辟出一块新的空间用来存放新的值,即使原先的对象被析构掉,释放内存了也不会影响到深拷贝得到的值。在自己实现拷贝赋值的时候,如果有指针变量的话是需要自己实现深拷贝的。
5.如果有一个空类,它会默认添加哪些函数?
-
无参的构造函数
-
拷贝构造函数
-
=运算符重载函数
-
析构函数
6.如何阻止一个类被实例化?
-
将类定义为抽象基类或者将构造函数私有化
-
不允许类外部创建类对象,只能在类内部创建对象
7.如何禁止程序自动生成拷贝构造函数?
-
为了组织编译器默认生成拷贝构造函数和拷贝赋值函数,我们需要手动重写这两个函数,某些情况下,为了避免调用拷贝构造函数和拷贝赋值函数,我们可以把它们设置为private,防止被调用。
-
类的成员函数和firend友元函数还是可以调用private函数,如果这个private函数只声明不定义,则会产生一个连接错误,所以我们定义一个基类,在基类中将拷贝构造函数和拷贝赋值函数设置成private,那么派生类中编译器中将不会自动生成这两个函数,且由于在基类中该函数是私有的,因此,派生类将阻止编译器执行相关的操作。
8.C++中为什么拷贝构造函数必须传引用不能传值?
假设拷贝构造函数是按值传递参数的话,会发生什么情况呢?比如,如果有一个类MyClass,拷贝构造函数写成MyClass(MyClass other),那么在调用这个构造函数的时候,就需要传递一个实参的副本。也就是说,当传入一个对象时,需要调用拷贝构造函数来创建参数other,而这个参数本身又是一个传值参数,这就会导致无限递归调用拷贝构造函数。
在C++标准中,明确指出拷贝构造函数的参数必须是引用类型,否则会导致编译错误。这是因为编译器检测到这种情况,会阻止用户这样定义,避免运行时的问题。
9.类成员初始化方式?构造函数的执行顺序?为什么用成员初始化列表会快一些?
-
赋值初始化,通过在函数体内进行赋值初始化;列表初始化,在冒号后使用初始化列表进行初始化。这两种方式的主要区别在于:对于在函数体中初始化,是在所有的数据成员被分配内存空间后才进行的。列表初始化是给数据成员分配内存空间时就进行初始化,就是说分配一个数据成员只要冒号后有此数据的赋值表达式,那么分配了内存空间后在进入函数体之前给数据成员赋值,就是说初始化这个数据成员此时函数体还未执行。
-
一个派生类构造函数的执行顺序如下:虚拟基类的构造函数(多个虚拟基类则按照继承的的顺序执行构造函数);基类的构造函数(多个普通基类也按照继承的顺序执行构造函数);类类型的成员对象的构造函数(按照成员对象在类中的定义顺序);派生类自己的构造函数。
-
方法一是在构造函数中做赋值的操作,而方法二是做纯粹的初始化操作。我们都知道,C++的赋值是会产生临时对象的。临时对象的出现会降低程序的效率。
10.简述下向上转型和向下转型
-
子类转换为父类:向上转型,使用dynamic_cast(expression),这种转换相对来说安全不会有数据的丢失。
-
父类转换为子类:向下转型,可以使用强制类型转换,这种转换是不安全的,会导致数据的丢失,原因是父类的指针或者引用的内存中可能不包含子类的成员的内存。
11.简述一下C++中的多态
由于派生类重写基类方法,然后用基类引用或指针指向派生类对象,调用方法时候会根据所指对象的实际类型来调用相应的函数,如果对象类型是派生类,就调用派生类的函数,如果对象是基类,就调用基类的函数,这就是多态。
#include <iostream>using namespace std;class Base{public:virtual void fun(){cout << " Base::func()" <<endl;}};class Son1 : public Base{public:virtual void fun() override{cout << " Son1::func()" <<endl;}};class Son2 : public Base{};int main(){Base* base = new Son1;base->fun();base = new Son2;base->fun();delete base;base = NULL;return 0;}// 运行结果// Son1::func()// Base::func()
子类1继承并重写了基类的函数,子类2继承基类但没有重新基类的函数,从结果分析子类 体现了多态性,那么为什么会出现多态性,其底层的原理是什么?
这里需要引出虚表和虚基表指针的概念。
虚表:虚函数表的缩写,类中含有virtual关键字修饰的方法时,编译器会自动生成虚表。
虚表指针:在含有虚函数的类实例化对象时,对象地址的前四个字节存储的指向虚表的指针。
实现多态的过程:
-
编译器在发现基类中有虚函数时,会自动为每个含有虚函数的类生成一份虚表,该表是一个一维数组,虚表里保存了虚函数的入口地址
-
编译器会在每个对象的前四个字节中保存一个虚表指针,即vptr,指向对象所属的虚表。在构造时,根据对象的类型区初始化虚表指针,从而让vptr指向正确的虚表,从而在调用虚函数时,能找到正确的函数。
-
所谓的合适时机,在派生类定义对象时,程序运行会自动调用构造函数,在构造函数中创建虚表并对虚表初始化。在构造子类对象时,会先调用父类的构造函数。此时,编译器只看到了父类,并为父类对象初始化虚表指针,令他指向父类的虚表;当调用子类的构造函数时,为子类对象初始化虚表指针,令它指向子类的虚表。
-
派生类的虚表生成:a.先将基类中的虚表内容拷贝一份到派生类虚表中 b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数 c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。
-
当派生类对基类的虚函数没有重写时,派生类的虚表指针指针指向的相当于基表指针,此时调用的是基类的虚函数,当派生类对基类的虚函数重写时,派生类的虚表指针指向的是自身的虚表,调用这个函数。
12.静态绑定和动态绑定
静态绑定: 静态绑定是在编译时确定调用的函数或方法,它是通过函数或方法的名称、参数数量、类型和顺序来匹配确定的。对于非虚拟函数和静态成员函数,默认情况下都是静态绑定。例如,在以下代码中:
class Base {public:void display() {std::cout << "Base class" << std::endl;}};class Derived : public Base {public:void display() {std::cout << "Derived class" << std::endl;}};int main() {Base baseObj;Derived derivedObj;baseObj.display(); // 静态绑定,输出 "Base class"derivedObj.display(); // 静态绑定,输出 "Derived class"}
动态绑定: 动态绑定是指在运行时确定调用的函数或方法,它是通过虚拟函数和指针/引用来实现的。虚拟函数是在基类中声明为虚拟的成员函数,在派生类中进行重写。通过使用基类的指针或引用调用虚拟函数时,实际调用的是派生类中重写的函数。例如,在以下代码中:
class Base {public:virtual void display() {std::cout << "Base class" << std::endl;}};class Derived : public Base {public:void display() {std::cout << "Derived class" << std::endl;}};int main() {Base* basePtr;Derived derivedObj;basePtr = &derivedObj;basePtr->display(); // 动态绑定,输出 "Derived class"}
13.基类的虚函数表存放在内存的什么区?虚表指针vptr的初始化时间?
C++中的虚函数表位于只读数据段,也就是C++内存模型中的常量区;而虚函数位于代码段,也就是C++内存模型的代码区。
由于虚表指针和虚函数密不可分,对于有虚函数或继承于拥有虚函数的基类,对该类进行实例化时,在构造函数执行时会对虚表指针进行初始化,并且存在对象内存布局的最前面。
14.构造函数为什么不能为虚函数?析构函数为什么要虚函数?
构造函数为什么不能为虚函数
-
从存储空间角度:虚函数对应一个指向vtable,这个表的地址是存储在对象的内存空间的。如果将构造函数设置为虚函数,就需要到vtable 中调用,可是对象还没有实例化,没有内存空间分配,如何调用。(悖论)
-
从使用角度:虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。
-
从实现上看,vbtl 在构造函数调用后才建立,因而构造函数不可能成为虚函数。从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数。
析构函数为什么要虚函数?
虚析构:将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。如果基类的析构函数不是虚函数,在特定情况下会导致派生来无法被析构。
-
用派生类类型指针绑定派生类实例,析构的时候,不管基类析构函数是不是虚函数,都会正常析构
-
用基类类型指针绑定派生类实例,析构的时候,如果基类析构函数不是虚函数,则只会析构基类,不会析构派生类对象,从而造成内存泄漏。为什么会出现这种现象呢,个人认为析构的时候如果没有虚函数的动态绑定功能,就只根据指针的类型来进行的,而不是根据指针绑定的对象来进行,所以只是调用了基类的析构函数;如果基类的析构函数是虚函数,则析构的时候就要根据指针绑定的对象来调用对应的析构函数了。
C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此C++默认的析构函数不是虚函数,而是只有当需要当作父类时,设置为虚函数。
15.请你回答一下 C++ 类内可以定义引用数据成员吗?
c++类内可以定义引用成员变量,但要遵循以下三个规则:
-
不能用默认构造函数初始化,必须提供构造函数来初始化引用成员变量。否则会造成引用未初始化错误。
-
构造函数的形参也必须是引用类型。
-
不能在构造函数里初始化,必须在初始化列表中进行初始化。
16.哪些函数不能是虚函数?
-
构造函数,构造函数初始化对象,派生类必须知道基类函数干了什么,才能进行构造;当有虚函数时,每一个类有一个虚表,每一个对象有一个虚表指针,虚表指针在构造函数中初始化。
-
内联函数:内联函数表示在编译阶段进行函数体的替换操作,而虚函数意味着在运行期间进行类型确定,所以内联函数不能是虚函数;
-
静态函数,静态函数不属于对象属于类,静态成员函数没有this指针,因此静态函数设置为虚函数没有任何意义。
-
友元函数,友元函数不属于类的成员函数,不能被继承。对于没有继承特性的函数没有虚函数的说法。
-
普通函数,普通函数不属于类的成员函数,不具有继承性,因此普通函数没有虚函数。
17.说说 C++ 中什么是菱形继承问题,如何解决
/**Animal类对应于图表的类A**/class Animal { /* ... */ }; // 基类{int weight;public:int getWeight() { return weight;};};class Tiger : public Animal { /* ... */ };class Lion : public Animal { /* ... */ }class Liger : public Tiger, public Lion { /* ... */ }int main( ){Liger lg ;/*编译错误,下面的代码不会被任何C++编译器通过 */int weight = lg.getWeight(); }
在我们的继承结构中,我们可以看出Tiger和Lion类都继承自Animal基类。所以问题是:因为Liger多重继承了Tiger和Lion类,因此Liger类会有两份Animal类的成员(数据和方法),Liger对象"lg"会包含Animal基类的两个子对象。
所以,你会问Liger对象有两个Animal基类的子对象会出现什么问题?再看看上面的代码-调用"lg.getWeight()"将会导致一个编译错误。这是因为编译器并不知道是调用Tiger类的getWeight()还是调用Lion类的getWeight()。所以,调用getWeight方法是不明确的,因此不能通过编译。
我们给出了菱形继承问题的解释,但是现在我们要给出一个菱形继承问题的解决方案。如果Lion类和Tiger类在分别继承Animal类时都用virtual来标注,对于每一个Liger对象,C++会保证只有一个Animal类的子对象会被创建。看看下面的代码:
class Tiger : virtual public Animal { /* ... */ };class Lion : virtual public Animal { /* ... */ }
18.说说什么是虚继承,解决什么问题,如何实现?
虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:其一,浪费存储空间;第二,存在二义性问题,通常可以将派生类对象的地址赋值给基类对象,实现的具体方式是,将基类指针指向继承类(继承类有基类的拷贝)中的基类对象的地址,但是多重继承可能存在一个基类的多份拷贝,这就出现了二义性。虚继承可以解决多种继承前面提到的两个问题。
虚继承底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。
实际上,vbptr指的是虚基类表指针,该指针指向了一个虚基类表,虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。
19.说说C++中虚函数与纯虚函数的区别
-
虚函数和纯虚函数可以定义在同一个类中,含有纯虚函数的类被称为抽象类,而只含有虚函数的类不能被称为抽象类。
-
虚函数可以被直接使用,也可以被子类重载以后,以多态的形式调用,而纯虚函数必须在子类中实现该函数才可以使用,因为纯虚函数在基类有声明而没有定义。
-
虚函数和纯虚函数都可以在子类中被重载,以多态的形式被调用。
-
虚函数和纯虚函数通常存在于抽象基类之中,被继承的子类重载,目的是提供一个统一的接口。
-
虚函数的定义形式:
virtual{}
;纯虚函数的定义形式:virtual { } = 0
;在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时要求前期绑定,然而虚函数却是动态绑定,而且被两者修饰的函数生命周期也不一样。
4、STL
5、C++新特性
相关文章:
C++题目
1、内存管理 1.内存模型 栈:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。 堆:就是那些由new分配的内存块,其释放由程序员控制(一个new对应一个delete)…...
Vulhub-jangow-01-1.0.1通关攻略
第0步: 打开靶机,按下shift,出现下图界面 在此页面按下e键,进入如下界面, 将ro 替换为 rw signie init/bin/bash 替换完毕后,按下Ctrl键X键,进入如下页面 ip a查看网卡信息 编辑配置文件网卡信…...
入剖析 Android Compose 框架的关键帧动画(keyframes、Animatable)(二十三)
深入剖析 Android Compose 框架的关键帧动画(keyframes、Animatable) 引言 在当今的 Android 应用开发领域,用户体验已成为衡量一款应用成功与否的关键因素之一。而动画作为提升用户体验的重要手段,能够为应用增添生动性和交互性…...
java中的枚举类型和c,c++的有区别吗?c,c++的枚举,结构体,联合体,三种数据有什么区别和联系
Java 枚举类型与 C、C 枚举类型的区别 1. 类型安全 Java:Java 的枚举类型是类型安全的。枚举常量是枚举类型的实例,编译器会严格检查传递的参数是否为该枚举类型的有效常量。例如: java Apply enum Color { RED, GREEN, BLUE } // 编译器会检…...
详解Redis的持久化与数据可靠性
Redis持久化与数据可靠性详解(结合实例) Redis作为内存数据库,持久化是保证数据不丢失的核心机制。它通过将内存数据保存到磁盘,确保服务器重启后能恢复数据。Redis提供RDB、AOF和混合持久化三种方式,下面通过实例和操…...
1、mysql基础篇--概述
关系型数据库(RDBMS) 概念特点:数据模型: 概念 建立在关系模型基础上,有多张表相互连接的二维表组成的数据库 特点: 1、使用表存储,格式统一,便于维护 2、使用sql语言操作&#…...
【Tiny RDM】Redis客户端工具
Tiny RDM Tiny RDM是一款现代化、轻量级、跨平台的Redis客户端,支持Mac、Windows和Linux,目前在Github上已有10kStar。 Github 项目地址: https://github.com/tiny-craft/tiny-rdm 功能特性 极度轻量,基于Webview2,…...
常见框架漏洞攻略-Shiro篇
漏洞名称 Shiro rememberMe反序列化漏洞 漏洞简介 Apache Shiro是⼀个强⼤易⽤的Java安全框架,提供了认证、授权、加密和会话管理等功能。Shiro框架直观、易⽤,同时也能提供健壮的安全性。 漏洞原理 在Shiro框架下,⽤户登陆成功后会⽣成…...
常见框架漏洞之一:Thinkphp5x
ThinkPHP是为了简化企业级应⽤开发和敏捷WEB应⽤开发⽽诞⽣的,是⼀个快速、兼容⽽且简单的轻量级国产PHP开发框架,诞⽣于2006年初,原名FCS,2007年元旦正式更名为 ThinkPHP,遵循Apache2开源协议发布,从Stru…...
MORL4PDEs:基于多目标优化与强化学习的数据驱动偏微分方程发现
摘要:本文提出了一种结合多目标优化与强化学习的数据驱动方法MORL4PDEs,用于从复杂系统观测数据中发现简洁的偏微分方程(PDE)。该方法无需预定义候选函数库,通过神经网络代理生成符号表达式,结合遗传算法优…...
UniApp和微信小程序中v-switch夜间模式动画开关
UniApp兼容版 <template><view><view class"main-container" :style"{ backgroundColor: value ? #45e3f9 : #20114c,transform:scale(${size})}" tap"onClick"><view class"content" :style"{ left: val…...
六十天Linux从0到项目搭建第四天(通配符命令、其他命令、压缩解压工具、shell的感性理解、linux权限解析)
通配符(Wildcard) 是 Shell 提供的特殊字符,用于 匹配文件名或路径名,可以代替一个或多个字符,使得命令能批量操作文件,而无需手动输入每个文件名。 典型用法 * 匹配任意字符 *.txt → 匹配所有 .txt 文…...
RAG优化:python从零实现自适应检索增强Adaptive Retrieval
开篇:当RAG遇上“自适应大脑”,检索从此不再“一根筋”!🧠 想象一下,你的AI助手是个超级聪明的“学霸”,但有时候却像个“一根筋”的机器人——无论你问它什么,它都用同一种方式去回答。问它“什么是XAI?”它给你一堆定义;问它“AI发展太快了吗?”它还是给你一堆定…...
C语言实现的冰墩墩
在windows系统下,vs 2022编译。 其中#include <graphics.h>需要自己下载安装。 环境配置没什么难度,直接上demo。 代码如下: #include <graphics.h> #include <conio.h> #include <math.h> #define PI acos(-1.0…...
【构建CV图像识别系统】从传统方法到深度学习
目录 1. 图像的基本概念1.1 像素与色彩1.2 过滤与卷积 2. 图像分类与检测3. 图像特征的提取3.1 全局特征3.2 局部特征3.2.1 边缘(Edge)3.2.2 角点(Corner)3.2.3 SIFT 特征 4. 传统方法与深度学习在图像识别中的应用4.1 基于传统方…...
在Centos 7环境下安装MySQL
前言:在安装与卸载MySQL时,用户需切换为root,这样安装之后,普通用户也能够使用。 Tips:我们在刚开始学习时,尽量全部使用root进行,适应mysql语句,后面学了用户管理,就可以考虑新建普…...
【机器学习基础 4】 Pandas库
一、Pandas库简介 Pandas 是一个开源的 Python 数据分析库,主要用于数据清洗、处理、探索与分析。其核心数据结构是 Series(一维数据)和 DataFrame(二维表格数据),可以让我们高效地操作结构化数据。Pandas …...
干部监督预警系统的定义与功能
一、干部监督预警系统是什么? 干部监督预警系统是通过整合多源数据(如干部档案、履职表现、廉政记录、舆情反馈等),利用大数据分析、人工智能等技术,对干部行为进行实时监测、风险评估和分级预警的数字化管理工具。 二…...
可视化图解算法:链表的奇偶重排(排序链表)
1. 题目 描述 给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。 注意是节点的编号而非节点的数值。 数据范围:节点数量满足 0≤n≤105,节点中的值都满足 0≤val≤10000 要…...
获取小红书笔记详情接口的详细指南
一、引言 小红书作为一个集社交、购物、分享于一体的综合性平台,拥有海量的用户和丰富的笔记内容。小红书笔记详情API接口为开发者提供了一种高效获取笔记详细信息的方法,包括笔记的标题、正文、图片、视频、标签、点赞数、评论数等。这些数据可以帮助开…...
麒麟系统运维指令
麒麟系统运维指令 麒麟系统运维指令1、 查看系统版本2、查看系统信息3、用户与权限管理4. 网络相关命令5. 包管理6. 文件操作7. 进程管理 麒麟系统运维指令 1、 查看系统版本 目的指令查看操作系统版本信息cat /etc/os-release查看操作系统版本信息hostnamectl查看内核版本un…...
pyqt SQL Server 数据库查询
一、概述 本项目旨在开发一个基于 Python 和 PyQt6 的数据库查询工具,该工具能够连接到 SQL Server 数据库,显示数据库中的表名,支持用户输入 SQL 查询语句进行数据查询,并将查询结果展示在表格中。同时,为了提升用户…...
抓包软件【Fiddler】
我叫补三补四,很高兴见到大家,欢迎一起学习交流和进步 今天来讲一讲Fiddler 什么是Fidder Fiddler是一款非常流行并且实用的HTTP抓包工具,原理是在电脑上开启一个HTTP代理服务器,然后转发所有的HTTP请求和响应。是用C#开发的工具…...
SpringCould微服务架构之Docker(1)
项目中微服务比较多的时候,一个一个手动的部署太麻烦了,所以就需要用到Docker。 项目部署中的问题: Docker是一种快速交付应用、运行应用的技术。...
计算图(Computation Graph)
在强化学习中,TensorFlow的计算图(Computation Graph)是用于描述模型结构和训练流程的核心机制。 1. 计算图的基本概念 定义:计算图是TensorFlow中表示数学运算和数据流动的有向图。图中的节点(Nodes)代表…...
邮件营销:如何设置合适的发送频率
在邮件营销里,把握好发送频率特别关键,这直接关系到客户愿不愿意搭理你的邮件,以及邮件营销能不能达到预期效果。下面这几个步骤和建议,能帮你找到合适的邮件发送频率: 一、了解目标受众 分析客户行为:查…...
React项目中,递归写法获取tree的id集合
后端接口返回一个childrens的树,最后要拿到的是每个childrens下第一个对象的id集合,用于编辑页的回显 采用的是递归写法!!!!!!!! const categoryIds: Array&…...
深入解析Linux网络、安全与容器技术
1. Netfilter:Linux内核的包处理框架 Netfilter 是Linux内核中用于控制网络数据包的核心机制,负责处理数据包的过滤、修改和转发。其核心功能包括: 包过滤(Packet Filtering):根据规则允许或拒绝数据包通过…...
AF3 Rotation 类解读
Rotation 类(rigid_utils 模块)是 AlphaFold3 中用于 3D旋转 的核心组件,支持两种旋转表示: 1️⃣ 旋转矩阵 (3x3) 2️⃣ 四元数 (quaternion, 4元向量) 👉 设计目标: 允许灵活选择 旋转矩阵 或 四元数 封装了常用的 旋转操作(组合、逆旋转、应用到点上等) 像 torch.…...
数据预处理习题
简述常用的文本数据类型。 结构化文本:如数据库中的表格数据、JSON/XML格式数据,具有明确的字段和层级关系。非结构化文本:如自然语言文本(新闻、社交媒体内容)、长文档(书籍、论文)࿰…...
常见框架漏洞—中间件IIS
一.IIS6.x篇 1.在Windows server 2003中搭建网站 2.访问网站,并对该网站进行抓包 3.修改提交方式为PUT,然后写入木马 4.修改提交方式为MOVE,令将其更名为脚本⽂档后缀 5.我们在Windows server 2003中可以看到我们上传的shell.asp 6.我们在网…...
群体智能优化算法-蚁狮优化算法(Ant Lion Optimizer, ALO,含Matlab源代码)
一、文章摘要 蚁狮优化算法(Ant Lion Optimizer,ALO)是一种新颖的元启发式算法,由Mirjalili提出,其灵感来源于自然界中蚁狮幼虫构筑陷阱捕猎蚂蚁的行为。该算法通过模拟蚂蚁的随机游走、蚁狮的陷阱机制、陷阱缩小及精…...
【计算机视觉】数据增强
一、数据增强的意义 在深度学习中,数据集往往有限,而模型需要大量的样本来学习特征。数据增强技术通过对图像进行如下变换: 扩充样本数量:利用已有数据生成新的样本。提高模型鲁棒性:使模型适应不同的图像变换&#…...
BERT文本分类实战----美团外卖评论情绪分类
HuggingFace 提供了巨大的模型库,虽然其中的很多模型性能表现出色,但这些模型往往是在广义的数据集上训练的,缺乏针对特定数据集的优化,所以在获得一个合适的模型之后,往往还要针对具体任务的特定数据集进行二次训练&a…...
Chrome 133 版本开发者工具(DevTools)更新内容
Chrome 133 版本开发者工具(DevTools)更新内容 一、持久化的 AI 聊天记录 AI 助手面板会在本地持久化聊天记录,即使重新加载 DevTools 或 Chrome,也可以查看之前与 Gemini 的对话内容。 二、Performance 面板改进 此版本为 Per…...
大模型应用(Java)2025/3/24
大佬视频👉使用Java实现一个基础的大模型RAG问答对话系统_哔哩哔哩_bilibili 需求 让大模型来理解知识库内容,并根据知识库回答。 通过本次应用我学到了: RAG工程的基本处理框架流程(基于java)向量数据库的基础使用…...
基于Sentinel-1A GRD洪涝淹没范围提取(SDWI阈值法和OSTU自动阈值法)
0 前言 两幅灾前和灾后的遥感影像经过SARscape配准、滤波、辐射定标预处理之后,使用GDAL库分别使用SDWI阈值法和OSTU自动阈值法提取洪涝淹没范围 1 ENVI 5.6和SARscape5.6安装 通过网盘分享的文件:ENVI5(1).6 链接: https://pan.baidu.com/s/1mKcEkC3…...
PPT 转高精度图片 API 接口
PPT 转高精度图片 API 接口 文件处理 / 图片处理,将 PPT 文件转换为图片序列。 1. 产品功能 支持将 PPT 文件转换为高质量图片序列;支持 .ppt 和 .pptx 格式;保持原始 PPT 的布局和样式;转换后的图片支持永久访问;全…...
IOS接入微信方法
导入SDK 和配置 SDK 的不做介绍; 1 在IOS 开发者中心 Identifiers 打开‘Associated Domains’ 2 建立一个文件(不带后缀的)apple-app-site-association, teamid在 IOS 开发者中心的会员找,appid在 xcode里面找 {"applin…...
隐式与显式等待的区别及混合使用
隐式等待(Implicit Wait)和显式等待(Explicit Wait)是 Selenium WebDriver 中两种不同的等待机制,用于处理动态加载的页面元素或异步操作。以下是它们的区别、作用范围以及混合使用的注意事项: 1. 核心区别…...
Selenium基本使用(三)隐藏框、获取文本、断言、切换窗口
1、定位文本框,密码框,按钮 案例一: 网站:过期更新 from selenium import webdriver import time dxwebdriver.Chrome() dx.get("过期更新") time.sleep(2) dx.find_element_by_name("userAccount").send_keys("…...
蓝桥杯,利用 Vue.js 构建简易任务管理器
在日常开发中,我们经常需要处理各种任务和计划。一个简单且高效的任务管理器可以帮助我们更好地组织和安排时间。今天,我将向大家展示如何使用 Vue.js 构建一个简易的任务管理器。这个项目不仅能够帮助我们更好地理解 Vue.js 的基本语法和功能࿰…...
vmwaretools解压失败|vmware tools distrib cannot mkdir read only file system|bug汇总
最简单的一条路线:你的解压命令用sudo了吗? 这个方法不能解决的话就看下面内容。本文提供给你全过程思路。 如需转载,标记出处 背景: 之前虚拟机和主机的复制黏贴还能用,今天突然用不了,重新下载安装包&am…...
类与对象(中)(详解)
【本节目标】 1. 类的6个默认成员函数 2. 构造函数 3. 析构函数 4. 拷贝构造函数 5. 赋值运算符重载 6. const成员函数 7. 取地址及const取地址操作符重载 1.类的6个默认成员函数 如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗&…...
2025-03-21 Unity 网络基础3——TCP网络通信准备知识
文章目录 1 IP/端口类1.1 IPAddress1.2 IPEndPoint 2 域名解析2.1 IPHostEntry2.2 Dns 3 序列化与反序列化3.1 序列化3.1.1 内置类型 -> 字节数组3.1.2 字符串 -> 字节数组3.1.3 类对象 -> 字节数组 3.2 反序列化3.2.1 字节数组 -> 内置类型3.2.2 字节数组 -> 字…...
练习8-8 移动字母
练习8-8 移动字母 day 8 void Shift( char s[] ){int lenstrlen(s);int a[3];for(int i0;i<3;i){a[i]s[i];}for(int i3;i<len;i){s[i-3]s[i];}s[len-3]a[0];s[len-2]a[1];s[len-1]a[2]; }...
BigEvent项目后端学习笔记(二)文章分类模块 | 文章分类增删改查全流程解析(含优化)
📖 模块概述 文章分类模块包括 新增文章分类、文章分类列表、获取文章分类详情、更新文章分类、删除文章分类 功能。本篇对于原项目进行了代码优化,将原先写在 Controller 层的业务逻辑代码迁移至了 Service 层。 🛠️ 技术实现要点 分组校…...
蓝桥杯,冬奥大抽奖
在日常的网页开发中,抽奖功能是一种常见的交互设计,它可以增加用户的参与感和趣味性。今天,我将分享一个简单的抽奖转盘实现,它使用了HTML、CSS和JavaScript,非常适合初学者学习和理解前端开发的基本概念。 一、项目背…...
scNET:整合scRNA-seq和PPI用于学习基因和细胞的embedding
scRNA-seq 技术的最新进展为深入了解各种组织的异质性提供了前所未有的视角。然而,仅靠基因表达数据往往无法捕捉和识别细胞通路和复合物的变化,因为这些变化在蛋白质水平上更容易被察觉。此外,由于scRNA-seq数据存在高噪声水平和零膨胀等固有…...
第四天 开始Unity Shader的学习之旅之Unity中的基础光照
Unity Shader的学习笔记 第四天 开始Unity Shader的学习之旅之Unity中的基础光照 文章目录 Unity Shader的学习笔记前言一、我们是如何看到这个世界的1. 光源2.吸收和散射3.着色 二、标准光照模型1. 自发光2. 高光反射① Phong模型② Blinn-Phong模型 3.漫反射4.环境光 总结 前…...