C++/游戏开发面试问题总结分享

今天给大家带来对C++/游戏开发面试问题总结分享,乐观估计可以涵盖80%左右的面试基础问题。

个人觉得如果这些问题你全部搞懂的话,大部分面试官在C++上就拿你没什么办法或者说不会再进一步为难你了。不过想彻底理解所有内容也并不容易,这里面涉及到操作系统、数据结构、计算机系统原理、汇编等基础内容,涉及到的书籍包括《C++ Primer》《Inside the C++ Object Model》《Effctive C++》《More Effctive C++》《C++ Template》《The Design and Evolution of C++》《STL源码剖析》《深入理解计算机系统》等。希望对想从事游戏程序开发等相关工作的朋友有所帮助~

问:了解const么?哪些时候用到const?与宏定义有什么差异?(提问概率:★★★★)

简单理解,const的目的就是定义一个“不会被修改的常量”,可以修饰变量、引用、指针,可以用于函数参数、成员函数修饰。成员变量。使用const可以减少代码出错的概率,我们通常要注意的是区分常量指针(指向常量的指针)和指针常量(地址是常量,指针指向的地址不变)以及合理的在函数参数里面使用。具体的情况可以参考下面的书籍与资料。

参考书籍与资料:《Effctive C++》

问:reference和pointer的区别?哪些情况使用pointer?(提问概率:★★)

1.指针可以为空,而引用不可以指向空值。

2.指针可以不初始化,引用必须初始化。这意味着引用不需要检测合法性

3.指针可以随时更改指向的目标,而引用初始化后就不可以再指向任何其他对象

根据上面的情况我们知道大概知道哪些时候需要使用指针了。不过还有一种情况,在重载如[]符号的时候,建议返回引用,这样便于我们书写习惯也方便理解。因为平时我们都是这样使用, a[10] = 10;而不是 *a[10] = 10;

参考书籍与资料:《More Effctive C++》

问:inline的优劣(提问概率:★★)

优点:减少函数调用开销

缺点:增加函数体积,exe太大,占用CPU资源,可导致cache装不下(减小了cache的命中) ,不方便调试debug下一般不内联, 每次修改会重新编译头文件增加编译时间

注意:inline只是一个请求,编译器有权利拒绝。有7种情况下都会拒绝,虚调用,体积过大,有递归,可变数目参数,通过函数指针调用,调用者异常类型不同,declspec宏等

forceinline字面意思上是强制内联,一般可能只是对代码体积不做限制了,但是对于上面的那些情况仍然不会内联,如果没有内联他会返回一个警告。 构造函数析构函数不建议内联,里面可能会有编译器优化后添加的内容,比如说初始化列表里面的东西。

问:final和override的作用,以及使用场合(提问概率:★★)

final:禁止继承该类或者覆盖该虚函数

override:必须覆盖基类的匹配的虚函数

场合(final):不希望这个类被继承,比如vector,编码者可能不够了解vector的实现,或者说编写者不希望别人去覆盖某个虚函数,顾名思义,final就是最终么

场合(override):第一种,在使用别人的函数库,或者继承了别人写的类时,想写一个新函数,可能碰巧与原来基类的函数名称一样,被编译器误认为要重写基类的函数。第二种情况是想覆写一个基类的函数,但是不小心参数不匹配或者名字拼错,结果导致写了一个新的虚函数

参考书籍与资料:《C++ Primer》

问:The rule ofthree是什么?为什么这么做?(提问概率:★)

If you need to explicitly declare either the destructor,copy constructor or copy assignment operator yourself, you probably need toexplicitly declare all three of them.(析构函数,拷贝构造函数,赋值运算符尽可能一起声明。如果你只定义一个,编译器会帮助你定义另外两个,而编译器定义的版本也许不是你想要的)

问:C++03/98有什么你不习惯或不喜欢的用法?C++11有哪些你使用到的新特性?(提问概率:★★★★★)

这个问题最简单的办法就是看下一个版本的C++有哪些特性,新的特性肯定是有意义的。

如:

auto,有一些迭代器或者map嵌套类型,遍历时比较麻烦,auto写起来很方便。

vector以及其他容器的列表初始化,原来想要像数组一样初始化的话,需要一个一个来,很麻烦。

类内初始值问题,总是需要放到构造函数里面初始化,初始化列表倒是不错,但是初始化数据太多就不行了。

nullptr,C++11前的NULL一般是是这样定义的 #define NULL 0,这可能会导致一些函数参数匹配问题。而nullptr可以避免这个问题。

thread,不需要再使用其他的库来写多线程了。

智能指针shareptr,一定程度上解决内存泄露问题。

右值引用,减少拷贝开销。

lambda function,简化那些结构简单的函数代码。

当然,你要是能说出一些还没有改正或者有待考虑的问题就更好了,比如内存管理的困难(没有GC),没有反射以及一些C#,java里面有而C++没有的特性等,要能深入一点说那就更好了

参考书籍与资料:《C++ Primer》

问:Delete数组的一部分会发生什么?为什么出现异常?(提问概率:★★★★)

VC下是异常,实际删除的时候整个数组的内存不仅仅是数据大小还包括CRTHeader,数组长度等信息。如果删除一部分会从数量的位置开始传入,是有问题的。VC下数组的内存布局参考下面公式,

公式1)_CrtMemBlockHeader + <Your Data>+gap[nNoMansLandSize];这类数据用delete和delete[]都一样!

公式2)_CrtMemBlockHeader +数组元素个数+ <Your Data>+gap[nNoMansLandSize];

如果其他编译器,有可能不会报错。但是只释放一个数组对象也是有问题的,其他的对象既没有释放也没有析构。

问:系统是如何知道指针越界的?(提问概率:★★)

VC下有一个结构体_CrtMemBlockHeader,里面有一个Gap属性,这个Gap数组放在你的指针数据的后面,默认为0xFD,当检测到你的数据后不是0xFD的时候就说明的你的数据越界了。

问:C++编译器有哪些常见的优化?听说过RVO(NRVO)么?(提问概率:★★★)

1.常量替换如int a = 2; int b = a; return b;可能会优化为 int b=2; return b; 进一步会优化为return 2;

2.无用代码消除比如函数返回值以及参数与该表达式完全无关,直接会优化掉这段代码

3.表达式预计算和子表达式提取常量的乘法会在编译阶段就计算完毕,相同的子表达式也会被合并成一个变量来进行计算

4.某些返回值为了避免拷贝消耗,可能会被优化成一个引用并放到函数参数里面,如RVO,NRVO。

RVO:函数返回的对象如果是新构造的值类型就直接通过一个引用作为参数来构造,进而避免创建一个临时的“temp”对象。

NRVO:相比RVO进一步优化。对于RVO,如果函数在返回前创建了一个临时变量,这个临时变量还是会被构造的,参考下面代码

Point3d Factory()

{

Point3d po(1,2, 3);

return po;

}

//RVO优化后

void Factory(Point3d &_result)

{

Point3d po(1,2,3);

_result.Point3d::Point3d(po);

return;

}

//NRVO优化后

void Factory(Point3d &_result)

{

_result.Point3d::Point3d(1, 2, 3);

return;

}

NRVO则直接跳过临时对象的构造。

(补充:上面的优化有的时候不同编译器可能有差别,想一探究竟建议查看反汇编代码。一般来说函数返回的临时值类型对象是右值,通过寄存器存储,所以获取不到地址)

当然,优化还有很多,这里不一一列举。由于这些优化,你在调试过程中可能无法设置断点,所以需要关闭优化。还有一个小的技巧,static变量不会被优化。

参考书籍与资料:

《Inside the C++ Object Model》(深度探索C++对象模型)

问:听说过mangling么?(提问概率:★★)

mangling 指编译器给函数变量等添加很多的描述信息到名称上用于传递更多信息。常用函数重载,编译时可以把返回值类型等与原函数名称进行组合达到区分的效果,具体规则看编译器。

参考书籍与资料:《Inside the C++ Object Model》(深度探索C++对象模型)

问:成员函数指针了解么?可以转换为Void*么?为什么?(提问概率:★★★)

不可以转换成Void*,因为成员函数指针大小并不是4个字节(32位机器上),除了地址还需要this的delta,索引等信息。成员函数指针比较复杂,建议好好读一下下面给出的文章。

写法:函数指针 float (*my_func_ptr)(int, char *);

成员函数指针 float (SomeClass::*my_memfunc_ptr)(int,char *);

问:描述一下C/C++代码的编译过程?(提问概率:★★★★)

预处理——编译——汇编——链接。预处理器先处理各种宏定义,然后交给编译器;编译器编译成.s为后缀的汇编代码;汇编代码再通过汇编器形成.o为后缀的机器码(二进制);最后通过链接器将一个个目标文件(库文件)链接成一个完整的可执行程序(或者静态库、动态库)。

参考书籍与资料:《深入理解计算机系统》

问:了解静态库与动态库么?说说静态链接与动态链接的实现思路(提问概率:★★★)

静态库:任意个.o文件的集合,程序link时,被复制到output文件。这个静态库文件是静态编译出来的,索引和实现都在其中,可以直接加到内存里面执行。

对于Windows上的静态库.lib有两种,一种和上面描述的一样,是任意个.o文件的集合。程序link时,随程序直接加载到内存里面。另一种是辅助动态链接的实现,包含函数的描述和在DLL中的位置。也就是说,它为存放函数实现的dll提供索引功能,为了找到dll中的函数实现的入口点,程序link时,根据函数的位置生成函数调用的jump指令。(Linux下.a为后缀)

动态库:包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。(Linux下.so为后缀)

参考书籍与资料:《深入理解计算机系统》

问:知道内部链接与外部链接么?(提问概率:★★)

内部链接:如果一个名称对于他的编译单元是局部的,并且在链接时不会与其他的编译单元中同样的名字冲突,那么这个名称就拥有内部链接。

外部链接:一个多文件的程序中,一个实体可以在链接时与其他编译单元交互,那么这个实体就拥有外部链接。换个说法,那些编译单元(.cpp)中能想其他编译单元(.cpp)提供其定义,让其他编译单元(.cpp)使用的函数、变量就拥有外部链接

问:extern与static(提问概率:★★★)

extern 声明一个变量定义在其他文件,这样当前文件就可以使用这个变量,否则会编译失败,如果两个全局变量名称一样会出现链接失败。extern c的作用更重要,因为c++的编译方式与c是不同的,比如函数重载利用mangling的优化。static变量,就是在全局声明一个变量判断是否初始化,是的话之后就不做操作了。static成员函数其实在编译后与class完全没有关系。static成员其实也没关系,但是private的需要通过类去调用。static全局只能在本文件使用(内链接),与其他无关。全局函数变量是外链接,可以跨单元调用。

参考书籍与资料:《C++ primer》

问:delegate是什么?实现思路?与event的区别?(提问概率:★★★)

代理简单来说就是让对象B去代理A执行A本身的操作,本质上就是通过指向其他成员函数或者全局函数的函数指针去代理执行。而函数指针有两种,成员函数指针与普通的函数指针,我们一般就是通过对这两种指针的封装来实现代理的效果。常见的实现方式有两种,一种是通过多态接口,另一种是通过宏。代理也分为单播代理与多播代理,单播就是一个调用只代理执行一个函数功能,多播代理就是一个调用可以绑定多个代理函数,可以触发多个代理的函数操作。

Event是一种特殊的多播delegate,只有声明事件的类可以调用事件的触发操作。最常见的也容易理解的就是MFC里面的按钮的鼠标点击事件了,他的调用只能在Button里面去执行。

问:使用过模板么?了解哪些特性?(提问概率:★★★★

模板分为函数模板与类模板,其根本目的是将类型“参数化”,实现编译时的“动态化”,避免重复代码的书写。另一种运行时的“动态化”就是多态。

模板使用常见的特性有“特化”,“偏特化”,“非类型模板参数”,“设置模板参数默认类型”,“模板中的typename的使用”,“双重模板参数Template Template Parameters”,“成员模板Member Template”,理解这些内容我们就基本上可以看STL标准库了。

另外,模板的实例化过程也是需要理解的。

参考书籍与资料:“STL源码”,《C++ Template》,《C++ Primer》

问:听说过转发构造么?(提问概率:★★)

通过foward关键字可以同时考虑到参数为左值以及右值的情况,然后把函数的参数完美的转发到其他函数的参数里面。这个里面涉及到左值、右值、move、forward、引用折叠等技术点。

参考书籍与资料:《C++ Primer》《Effective Modern C++》

移动语义(move semantic)和完美转发(perfect forward)

问:描述一下函数调用过程中栈的变化(提问概率:★★★★)

回答这个问题需要对栈的使用过程,函数调用,汇编都有一定的理解才行。首先,要清楚一个概念“栈帧”。

栈帧(stack frame):机器用栈来传递过程参数,存储返回信息,保存寄存器用于以后恢复,以及本地存储。为单个过程(函数调用)分配的那部分栈称为栈帧。栈帧其实是两个指针寄存器,寄存器ebp为帧指针(指向该栈帧的最底部),而寄存器esp为栈指针(指向该栈帧的最顶部)。

然后我们再简单描述一下函数调用的机制,每个函数有自己的函数调用地址,里面会有各种指令操作(这端内存位于“代码段”部分),函数的参数与局部变量会被创建并压缩到“栈”的里面,并由两个指针分别指向当前帧栈顶和帧栈尾。当进入另一个子函数时候,当前函数的相关数据会被保存到栈里面,并压入当前的返回地址。子函数执行时也会有自己的“栈帧”,这个过程中会调用CPU的寄存机进行计算,计算后再弹出“栈帧”相关数据,通过“栈”里面之前保存的返回地址再回到原来的位置执行前面的函数。参考下图:

参考书籍与资料:《深入理解计算机系统》

问:__cdecl/__stdcall是什么意思(提问概率:★★★)

常见的函数调用有如下

__cdecl/__stdcall/__thiscall/__fastcall。

cdecl按照c语言标准,从右到左,可以实现可变参数,调用者弹出参数。

stdcall(pascal调用约定)按照c++标准,函数参数从右到左,不支持可变参数,函数返回自动清空。但是有的时候编译器会识别并优化成cdecl。

Pascal语言中参数就是从左到右入栈的不支持可变长度参数

(注:__stdcall标记的函数结束后,ret 8表示清理8个字节的堆栈,函数自己恢复了堆栈)

参考书籍与资料:“建议查看反汇编代码”

问:C++中四种Cast的使用场景是什么?(提问概率:★★★★★)

constcast,去掉常量属性以及volatile,但是如果原来他就是常量去掉之后千万不要修改;比如你手里有一个常量指针引用,但是函数接口是非常量指针,可能需要转换一下;成员函数声明为const,你想用this去执行一个函数,也需要用constcast

staticcast,基本类型转换到void,转换父类指针到子类不安全

dynamiccast,判断基类指针或引用是不是我要的子类类型,不是强转结果就返回null,用于多态中的类型转换

reintercast,可以完成一些跨类型的转换,如int到void*,用于序列化网络包数据

参考书籍与资料:《C++ Primer》《The Design and Evolution of C++》(C++语言的设计与演化)

问:用过或很熟悉的设计模式有哪些?(提问概率:★★★★)

这个问题看好书写写代码就可以自由发挥了,下面给几个例子。

工厂模式,通过简单工厂生成NPC对象,简单处理的话可通过“字符串匹配”动态创建对象。如果有“反射机制”就可以直接传class来实现。当然可以进一步使用抽象工厂,处理不同的生产对象。

单例,实现全局唯一的一个对象。构造函数、静态指针都是私有的,使用前提前初始化或者加锁来保证线程安全。

Adaptor适配器,代码适配原来的相机移动最后调用的是原来的移动,现在加了适配器继承里面放了当前引擎的摄像机,然后覆盖原来摄像机的移动逻辑。

Observer,一个对象绑定多个观察者,然后这个对象一旦有消息就立刻公布给所有的观察者,观察者可以动态添加或删除。在UE4里面,行为树任务节点请求任务后进入执行状态,然后会立刻注册一个观察者observer到行为树(行为树本身就相当于前面提到的那个对象)的observer数组里面同时绑定一个代理函数。行为树tick检测消息发送给所有观察者,观察者收到消息执行代理函数。

参考书籍与资料:《Head First设计模式》《设计模式:可复用面向对象软件的基础》

(0)

相关推荐

  • 2021.2.1学习日志

    引用本身不是一个对象,因此不能定义指向引用的指针.但指针是对象,所以存在对指针的引用: int i=42;int *P;// p是一个int型指针int *&r = P;// r是一个对指针p ...

  • C 学习笔记1-const前缀

    C 学习笔记1|const前缀.static前缀 1. 使用方法 const int a = 10; int const a = 10; 这二者是等价的. 2. const的含义 const关键字是c ...

  • C++中const的用法

                         时间:2021-02-27 13:32:39         简介 Const 是C++中常用的类型修饰符,常类型是指使用类型修饰符const说明的类型,常类 ...

  • C语言常见问题

    C语言常见问题

  • 做游戏开发需要学什么,小A同学分享!

    近几年来游戏行业发生了翻天覆地的变化,以至于很多人现在都想加入这个行业,这个行业给大家的第一感觉就是高工资,这无形之中就给游戏开发者蒙上一层很厉害的面纱.有的人为了换个环境,有的人是真心喜欢游戏这个行 ...

  • 游戏引擎开发、设计实战分享…现在你可以半价买到这些专业书了

    总有一本适合你.文/龙之心今年年初,葡萄君曾做了一项统计,其中提到2020年人才走势一部分会体现在,头部游戏公司对技术.美术和TA(技术美术)的需求越来越明显.同时在分工精细的岗位面前,了解更多技术细 ...

  • 商业游戏开发计划书

    商业游戏开发计划书

  • 游戏开发行业如何合理税筹

    互联网的飞速发展,游戏这个行业也越来越大,消费群体的增加,各层次的人群喜爱的游戏也不一样,多样化成了游戏开发者的目的.像国内巨头公司,腾讯.网易等每年的收益大家都是看的到的,一款新的游戏开发上市之后, ...

  • 大触专访丨参与天刀、剑灵等大厂的3D游戏设计大佬个人心得分享,助你突破桎梏!

    图片素材来自网络,版权归原作者,仅供交流 ►本文正文约1.6k字,阅读时间约6分钟 huifeng huang A站:www.artstation.com/wilsonhuang 0 请向大家做下自我 ...

  • 游戏开发与设计中的“3C”是指什么?

    随着国内游戏行业的不断发展,越来越多国外的专业词汇被引入进来."3C"作为一个比较重要的技术名词至今却仍然被不少游戏从业者所陌生.那么到底什么是3C?他在游戏设计以及游戏开发中有什 ...

  • 【商业教程】使用unity3D 5 继续拧游戏开发全入门

    --  微资讯 · 微课程  -- 利用零碎时间,走上超神之路! 课程介绍 1课程介绍 2编程和UI介绍 3创建一个2D游戏 4创建一个恐怖的第一人称的设计游戏 5为3D设计游戏创建关卡 6为3D设计 ...

  • 【商业教程】unity5 2D游戏开发教程2套合集

    --  微资讯 · 微课程  -- 利用零碎时间,走上超神之路! 课程介绍 4小时提升你的游戏设计 课程描述 在这个课程中我们会使用简单的2D动作平台,你会学到多种技巧,比如如何添加 屏幕抖动,如何让 ...

  • 前言:为什么要学习微信小游戏开发?

    "如果小程序产品是力求做得垂直,痛点明确,目标用户群体明确:那么小游戏产品,则是力求做得宽泛,受众群众越多越好." 目录 1.<跳一跳> 2.<围住神经猫> ...