答:改变了对象的生命周期,使其出现在定义的时候到程序结束的整个时间段内,同时改变了对象的作用范围,使其只在定义它的文件内有效。
思路:作用+在类中使用的注意事项+与普通全局变量的异同
作用:保持变量内容持久,作用于局部变量时改变了局部变量的生存周期,使得变量存在于在定义后直到程序运行结束的这段时间;隐藏,作用于全局变量和函数,改变了他们的作用域,使得全局变量和函数只在定义它的文件内有效,在源文件中不具有全局可见性;作用于类的成员变量和成员函数,使得类变量和类成员函数和类有关,可以不定义类对象直接访问类的静态成员
在类中使用的注意事项:静态成员变量是在类内进行声明,在类外进行定义和初始化,在类外进行定义和初始化的时候不要出现static关键和和private、public、protected访问规则;静态成员相当于类域中的全局变量,被类的所有对象共享;静态成员变量可以作为成员函数的参数,普通成员变量不可以
与普通全局变量的异同:
同–都是静态存储方式
异–作用域不同、初始化不同-静态全局变量只初始化一次
答:表示对象本身的一个指针
思路:是什么+使用+创建+有效范围
是什么:每一个对象都能通过this指针访问自己的地址。
this指针是所有成员函数的隐含参数,在成员函数内部可以通过this指针来调用对象。
使用:this只能在成员函数中使用,成员函数的第一个参数为T* const register this;但是静态函数不能使用this指针,因为静态函数不属于具体一个对象,是整个类范围意义上的信息
创建:在成员函数开始执行前构造,在成员执行结束后清除
有效范围:只有在成员函数内才有定义,不能通过对象使用this指针,也无法知道对象this指针的位置,只有在成员函数中才可以
虚析构函数:为了当一个基类指针删除一个派生类的对象的时候,派生类的析构函数能够被调用,否则调用的会是基类的析构函数,会存在内存泄露
虚拟继承:菱形继承情况下会对同一基类的多次存储造成资源浪费,还造成了数据的二义性,虚拟继承解决了这一问题,虚继承的部分放在共享区域上
思路:编译+是否为函数+类型检查
编译:内联函数在编译的时候展开,宏在编译预处理的时候展开;编译时,内联函数直接嵌入代码,宏只是简单的文本替换
是否为函数:内联函数是真正的函数,只不过在调用点处直接展开,而宏是关键字
类型检查:inline有类型检查,宏没有
引用可以看做是一个变量的别名,编译器把用户的引用 *** 作看做const 指针;
引用的使用可以减少临时对象的拷贝
迭代器失效:由于元素空间重新分配导致之前的迭代器访问的元素不在了
7、在什么情况下会发生拷贝1、明确表示由一个对象初始化为另一个对象时
2、当对象作为函数的实参传递给形参时
3、当对象作为函数的返回值时
思路:栈、堆、全局\静态存储区、常量存储区、代码段
栈:存放局部变量、函数参数、返回地址,由编译器自动分配和释放
堆:动态申请的内存空间,有malloc分配的内存块,由程序员控制分配和释放,在程序结束后还没有释放 *** 作系统会回收
全局/静态存储区:存放全局变量和静态变量,程序运行结束 *** 作系统自动释放
常量存储区:存放常量,不允许修改,程序结束后自动释放
代码区:存放代码,不允许修改,可以执行
思路:是什么+干什么+对字符数组的处理+生效时间+参数类型
sizeof | strlen | |
---|---|---|
是什么 | C++运算符 | 头文件函数 |
干什么 | 测量字符数组分配的大小 | 测量字符串的实际长度 |
对字符数组的处理 | 字符数组作为形参,当做字符指针 | 依然是字符数组 |
生效时间 | 编译时计算长度 | 程序运行期间(库函数) |
参数类型 | 可以是类型也可以是变量 | 必须是char*类型 |
思路:什么是右值+什么是右值引用+move()
什么是右值:表达式结束后就不再存在的临时对象
什么是右值引用:绑定到右值的引用,通过&&获得,右值引用只能绑定到一个将要销毁的对象上,因此可以自由地移动其资源
move():可以将一个左值强制转化为右值,继而可以通过右值引用使用该值
作用:在拷贝构造的时候会有大量的构造和析构的调用,通过右值引用可以延长临时对象到函数中,避免了一些构造和析构。
在复制的时候也可以直接指向现有数据完成复制(C++ Primer P653)
int a = 42; // a 为左值, 42是右值
什么是右值:
11、指针和引用的区别指针 | 引用 | |
---|---|---|
指向内容 | 指针指向的内存空间在程序运行过程中可以改变 | 引用所绑定的对象不能改变 |
内存空间 | 占据内存空间 | 相当于变量别名,不占内存空间 |
能否为空 | 可以为空 | 必须绑定对象 |
能否多级 | 可以多级指针 | 引用只能一级 |
vector:序列式容器,三个普通指针扮演的迭代器构成vector数据结构,分别为指向空间头的iterator、指向目前空间尾的iterator和指向可用空间尾的iterator,维护一个连续的线性空间
list:是一个双向链表,只需一个指向node节点的指针,可以前后移动,每次插入和删除元素就配置和释放一个空间,迭代器不会失效
deque:双向开口的连续线性空间,
stack:先进后出的数据结构,deque封闭头端开口就是stack
queue:先进先出的数据结构,封闭deque底部出口,头部入口就是queue
heap:priority_queue的助手
priority_queue:底层是vector,使用heap形成的算法,以任何次序将元素推入容器,从优先级最高的元素开始取
slist:双向链表
map:红黑树
思路:默认构造函数+带参构造函数+拷贝构造函数+类型转换构造函数+显示构造函数
默认构造函数:创建对象不传参,编译器会调用默认构造函数对对象进行默认初始化;编译器会在没有定义构造函数的类自动生成一个构造函数;定义了任意一个构造函数的类,编译器不会生成默认构造函数;默认构造函数可以由用户自己定义;如果一个类有一个成员对象,成员对象没有默认构造函数,这时候编译器不知道如何初始化在这个成员对象;最好自定义默认构造函数;
带参数构造函数:最普通的构造函数,利用传入参数初始化对象,有初始化列表这种写法
拷贝构造函数:在发生值拷贝的时候被调用,和赋值函数分清;如果没有定义拷贝构造函数,编译器会自动生成一个(浅拷贝)
Dog h(2, name); // 带参构造函数
Dog x1 = h; // 拷贝构造函数
Dog x2;
x2 = h; // 赋值重载函数operator=()
类型转换构造函数:当构造函数只有一个参数的时候,且这个参数不是本类的const引用时,这种构造函数为转换构造函数
显示构造函数:限制构造函数的行为阻止奇怪的类型自动转换
思路:什么是内存拷贝+拷贝vector会怎么样
什么是内存拷贝:内存拷贝函数void*memcpy(void*dest, const void*src,unsigned int count);
,由src所指向的内存区域复制count个字符到dest所指的内存区域。
拷贝vector会怎么样:复制后两个vector中的元素指向同一个地址,在析构的时候会出现指针被析构两次的错误,正确的做法应该是对复制的目标容器预先分配空间,这样在复制时不会指向一个地址
对于基本类型,深拷贝和浅拷贝是一样的,都是对原始数据的复制,修改原始数据不会对复制的数据产生影响
当数据成员中有指针时,浅拷贝使两个类中的两个指针指向同一个地址,当两个对象析构的时候会调用两次析构函数,导致指针悬挂的现象,这时候需要深拷贝。
区别:深拷贝会在堆内存中另外申请空间来存储数据
①准备参数(计算参数、传参)
②保存返回地址
☂控制转移至callee
④保存必要的caller现场
思路:什么是帧同步+状态/帧同步
什么是帧同步:需要多个客户端表现效果一致(如王者荣耀十个玩家的屏幕显示的英雄位置完全相同、技能释放角度、释放时间完全相同)。
对大多数游戏,不仅客户端表现要一致,客户端和服务端的数据也要一致。
状态同步和帧同步的区别:核心在战斗逻辑在客户端还是服务端
状态同步:战斗逻辑在服务端,服务端接受客户端的 *** 作请求,服务端通知客户端具体 *** 作,并通知后续可能的效果
帧同步:服务端只负责转发客户端的信息
状态同步 | 帧同步 | |
---|---|---|
流量消耗 | 大,每次更改都要同步,一个 *** 作服务端需要通知客户端多次 | 小,不需要同步属性,一个 *** 作只需服务端转发一次 *** 作 |
回放和观战 | 更难,需要一个回放&观战服务器,游戏中服务器给客户端发消息的同时还需要把消息发给回放&观战服务器,在其他客户端申请回放或观看是,回放&观战服务器会把存储的消息发给客户端 | 只需保存每局所有人的 *** 作 |
安全性 | 高,因为所有逻辑和数值都在服务端,想作弊需要攻击服务器,比更改客户端困难很多 | 因为所有数据都在客户端,解析客户端数据就可以(moba全图挂、吃鸡透视挂),可以通过比对同局其他人的战斗结果预防 |
服务器压力 | 大 | 小 |
开发效率 | 占据主流,但是开发难度大,同一个功能需要客户端服务端共同完成 | 开发难度低,PvP和PvE基本是相同的代码,服务器只需转发即可 |
断线重连 | 把整个场景和人物根据服务端的数据重新生成一遍 | 服务端把断线时刻到重连时刻的所有数据传给客户端,客户端加速游戏运行速度直到追上现有速度 |
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)