找回密码
 立即注册
查看: 160|回复: 5

如何筹备Unity游戏开发的面试?

[复制链接]
发表于 2024-7-15 18:02 | 显示全部楼层 |阅读模式
如何筹备Unity游戏开发的面试?
发表于 2024-7-15 18:02 | 显示全部楼层
前言

在面试中,我们经常会被问各种”莫名奇妙”的问题, 比如这道:”你是如何做好Unity项目性能优化的?”。这个问题也太泛了吧,没有具体的优化点,这怎么回答?” 瞬间跃入脑海。做面试复盘的时候,你可能会想这个面试官是不是什么都不懂,是个”青铜”啊。没错,能问这道问题的面试官要么是个”青铜”, 要么就是”王者”。说他是青铜,可能真的什么都不懂,只是他听人说Unity 项目做性能优化的时候比较麻烦,所以就来问你,看你如何处理。还有一类是王者,就是来考你各方面的综合能力,接下来我们从


“性能优化”最重要的方式(没有之一),不是任何的编程技术与技巧,而是从”项目管理”上防止”游戏做完马上要上线”时出现的性能问题。在日常项目管理中,把性能监控起来,问题尽早发现,尽早动态清零,这样才能做到项目从性能风险上是可控的。我的一些经验如下:
a:用心做好技术调研


b:从项目正式开发的第一天起就引入多平台测试与完整的测试机制;


c:留出专门时间与专人review代码;


d:了解运行中技术参数的变化,做好技术参数统计;


2:性能问题定位与分析
当项目开发过程中发现性能问题后,如何定位性能问题,定位到具体是哪里引发的,这个就变得非常重要了目前主要的手段如下(按照我认为的重要程度):




3:性能问题常用的解决方案
说到这里了,才是我们很多面试的同学一开始就回答的,可以用xxxx手段来解决xxx问题。可能我们说的都对,但是在”王者”面试官看来,他出这个面试题,目的就是看你是否从系统上去思考如何把控游戏项目的性能。定位到了是某方面的问题,再去应对一些常用的手段,这里我就把一些常用的列举一下,可能有遗漏。




如果你是面试官,看到一个年轻的小伙,能系统的思考”性能优化”这个工程问题,你会录取么?
Unity / 面试专区  1~3年Unity开发人员跳槽规划与面试准备

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2024-7-15 18:03 | 显示全部楼层
hello,我是一名练习时长超过两年半的游戏研发工程狮(  ̄□ ̄||),目前在猪厂工作,分享下我个人的面试经验。
(首先,题主并没有说是校招或者社招,这两者差别很大,校招看中基础,社招看中项目。我就说说我最熟悉的校招吧。)
1. 算法

不管是大中小厂,几乎都会在面试中问到算法,小到思路,大到手撕。以我面试猪厂的经历来看,开局先丢一道题手撕,做不出来基本没有后续。总共二面关于算法的思路问了三四题,可以看出大厂对算法的重视程度。(字节,快手的面试也是这种体验)
在面试之前我刷的题目(可作为参考)

  • 慕课网liuyubobobo的数据结构和算法课程(我目前认为讲的最好的数据结构课程,不好的地方是要付费)
  • 剑指offer的题目刷两遍(第一遍啥都不会,做不出来就看答案,第二遍自己写最优解)
  • leetcode 热门100
  • 排序算法(插入冒泡快排归并桶排必须能手写)
  • 常用数据结构(队列、链表、栈、堆排底层,我面试的时候让我写个栈,很常见的问题)
  • 大数据相关(哈希、位图、桶)
以上这些,如果能够基本熟练,可以大胆的挑战大厂面试,基本写不出来也能说个最优思路。
2. 基础知识

这部分我在面试快手、字节跳动的时候问得特别具体,感觉是很考验基本功的,一定不要死记硬背,要理解知识点背后的逻辑。
3. 项目经验


  • 比赛
现在很多厂家都会开一些比赛,比如什么高校精英挑战赛,或者各种面向学生的比赛。如果能得奖,基本上面试这家公司都是直通车了。如果实在没有得奖,面试的时候和面试官说自己参加了,也会大大加分。
我当时不懂这些,现在想想真的很后悔,入职工资绝对up up。
2. 游戏项目
在b站看到很多同学自己开发独立游戏,这是非常有意思的尝试,完全可以作为简历中的亮点写进去。最近我自己也在出一系列免费的游戏课程,如果有时间的同学完全可以提前开始尝试游戏制作,不仅可以作为面试谈资,也能积累项目经验。

总结

不管谈多少次面试方法,准备方法,学习方法,其实说到底都是术,而不是道。
道就是:立即开始行动,坚持不懈
以道驭术,才能收货最终的成功,加油!

我是小棋,一名游戏研发攻城狮,欢迎关注我,我们共同进步! <a class="member_mention" href="http://www.zhihu.com/people/6924f50d4b7e97028fba36c69ccd5052" data-hash="6924f50d4b7e97028fba36c69ccd5052" data-hovercard="p$b$6924f50d4b7e97028fba36c69ccd5052">@打工人小棋
发表于 2024-7-15 18:03 | 显示全部楼层
你好题主,一般在Unity面试的过程中面试官会考察很多方面。这里我参考了一下几个大厂朋友的面试经验贴,总结了一些面试需要注意的点,希望可以帮到你顺利通过面试。
1.首先就是自我介绍,自我介绍基本是面试官了解你经历、能力的首要手段,因此一个表达流利,逻辑紧密,内容丰富的自我介绍可以加不少的第一印象分。我们的自我介绍需要向传递表达以下几个方面的信息:
学历背景(向面试官传达你优秀的学历)、工作履历(向面试官传达你的项目经验、技能掌握的程度)、个人优势(对自己能力的一个总结)。
以上三个方面的要素,请记住每一句话都不能是废话,都要通过自己的经历向面试官传达自己的优势和能力。这里我可以给大家简单举个例子:
“我曾今带过有带过团队开发某款游戏的经历,这款游戏对某方面技术的要求非常高,我们在开发的前期充分评估了可能出现的问题,最终结合整个项目分析出问题所在,通过整个团队的努力最终成功的完成了整个项目。”
看似一段简单的关于工作经历的描述,其实非常严谨的向面试官展现了:
(1)你的团队领导以及协作能力
(2)你的技术能力(业务能力)
(3)解决问题的能力
(4)你是否具备成功的项目经验
2.接下来就是面试官会对你技术能力的考察,这部分主要分为两个维度:
(1)既定的面试问题,这块儿我已经贴心帮大家整理大厂的面试真题(附大厂总程的解答与拓展)点击下方小卡片领取即可!

点击下方小卡片领取面试真题、简历模板
(2)根据你的项目经验、DEMO提出的一些综合性的问题,因此这里就需要我们对自己demo以及项目经验一定要用心总结,而且当我们的DEMO、项目经验体现出来能力一定需要迎合目标公司,这都需要提前做好功课。
最后也是最重要的,就是一定重视基础很多朋友准备面试时,可能因为过于紧张或太过于倾向DEMO和项目经验,结果被问到一个基础问题突然大脑一片空白了。所以无论任何时候底层基础能力一定是需要重视的!
发表于 2024-7-15 18:04 | 显示全部楼层
游戏开发程序岗面试题答案版C++篇,后续继续更新游戏逻辑篇、unity篇、图形学篇,并整理成文档,可在公号【游戏君五尘】获取。
知乎排版较乱,原文链接
游戏开发面试答案篇(一)-- C++篇
游戏开发面试答案篇(二)-- Unity篇目录
一、基础语法   
二、面向对象   
三、内存   
四、STL
五、C++11新特征


一、基础语法
1、C 和 C++的区别
i.  C++是面向对象的的编程语言,C是面向过程的编程语言
ii. C++中的内存分配运算符是new/delete而C 中是malloc和free
iii. C++中有函数重载而C 中没有
iv. C++中新增了引用的概念而C 中只有值和指针

2、struct 和 class的区别
i. struct 一般用于描述一个数据结构集合而class是对一个对象数据的封装
ii. class 默认访问修饰符是私有的而struct 是公有
iii. 在继承方面 class默认是私有继承而struct 默认是公有继承

3、define宏定义 和 const 的区别
i. 首先 宏定义是在编译的预处理阶段起作用而const是在编译、运行时起作用
ii. 其次 宏定义它只做替换,并不会进行检查,很容易报错而const有数据类型,编译器会对它进行类型检查
iii. 最后 宏定义的数据没有分配内存,只是插入替换
而const定义的变量只是值不能变,但是会分配内存

4、define宏定义 和 line内联函数的区别
i. 首先 宏定义在预处理阶段起作用,只做简单的字符串替换,它没有返回值而 内联函数在编译阶段起作用,有返回值
ii. 然后 内联函数在编译时直接将函数代码嵌入到目标代码中,省去了函数调用的开销,从而提高性能,并且可以重载
iii. 最后 编译器会对内联函数进行类型检查以及语法判断
而宏定义不会

5、指针和引用的区别
i. 指针是一个变量,里面存放的是地址而引用是变量的别名,它和原来的变量实际上是同一个东西ii. 指针可以有多级而引用只能有一级
iii. 指针在初始化依然可以改变指向而引用初始化后就不能改变
iv. 对指针取地址,得到的是指针原本的地址
而对引用取地址,得到的是变量的地址

6、数组和指针的区别
i. 首先 数组是存储多个相同数据类型的集合。数组名是首元素的地址而指针是变量,用于存放其它变量在内存中的地址。指针名指向内存的首地址
ii. 其次 数组在内存中是连续存储的,通过数组下标进行访问,数组不是在静态区就是在栈上而指针的存储的存储空间不能确定iii. 最后 用sizeof计算数组,得到的是整个数组的大小
而指针得到的是该指针变量的大小

7、数组指针和指针数组的区别
数组指针:它是指向数组的指针,它的本质是指针,只不过指向数组中的某一个元素指针数组:它是存放指针的数组,其本质是数组,只不过其中存放的元素是指针
数组指针写做 int(*ptr) [4]
指针数组写做 int* ptr[4]

8、深拷贝和浅拷贝的区别
原因:在拷贝构造的过程中,导致两个对象指向同一块内存区域,这样再释放内存的过程中就会导致内存资源重复释放浅拷贝因为浅拷贝只是拷贝了一个指针,并没有新开辟一块内存区域所以就导致了两个对象指向了同一块地址最后会导致内存资源的重复释放深拷贝深拷贝是直接开辟了一个新的空间,新对象指向这个新的空间
这样 即使原对象被析构,也不会影响到新对象

9、移动构造和拷贝构造的区别
i. 首先 在拷贝构造函数中,如果涉及到指针,就需要用到深拷贝而在移动构造函数中,则用的是浅拷贝
ii. 最后 拷贝构造函数的参数是一个左值引用
而移动构造函数的参数是一个右值将亡值的引用

10、重载,重写,隐藏的区别
i. 首先,只有同一范围定义的同名函数才存在重载关系重载特点是函数名相同参数类型和数目不同
ii. 然后,重写是指子类覆盖父类中的同名函数,要求子类函数必须是虚函数,且与父类的虚函数有相同的参数类型,参数个数,以及返回值类型
iii. 所以,重写和重载的区别重载是函数之间的关系,重写是子类和父类的关系重载要求参数类型和数目不同,重载要求参数列表和返回值相同iv. 最后隐藏是指,子类中的函数屏蔽了父类中的同名函数。隐藏发生条件如下两者函数参数相同,但父类函数不是虚函数。与重写的区别在于父类是否为虚函数
两者参数不同,不管父类是不是虚函数,都会隐藏

11、指针常量和常量指针的区别
这里以以C++primer为准
指针常量:写作int const *p,指针本身是一个常量,它的值不能修改
常量指针:写作int* const p ,指针的指向是一个常量,它的指向不能修改

12、野指针和悬空指针区别
首先,它们都是指向无效内存区域的指针
野指针:指向的位置不确定
原因:指针定义时没有进行初始化
解决:定义时就进行初始化或置空悬空指针:指向的内存区域被释放
原因:指针指向的内存被释放,但指针没有及时置空
解决:内存释放后及时置空

13、静态类型和动态类型的区别
静态类型对象在声明时采用的类型,在编译期就已经确定动态类型
一个指针或引用目前所指对象的类型,在运行期时才能确定

14、源文本到文本可执行文件经历的过程
C++从源码到指向文件有四个过程:预处理、编译、汇编、链接
i. 预处理过程如下将所有的#define删除,并且展开所有的宏定义处理一些预编译指令,如#ifndef、#ifdef等处理#include预编译指令,将被包含的文件插入到该预编译指令的位置过滤所有的注释添加行号和文件名标识#ifndef、#ifdef的作用是防止重复包含头文件#include<> ,从标准库中寻找头文件。#include"",从当前目录开始寻找头文件。
ii. 编译过程分为6步词法分析:将源代码的字符序列分割成一系列的记号语法分析:对记号进行语法分析,产生语法树语义分析:判断表达式是否有意义代码优化生成汇编代码汇编代码优化
iii. 汇编:这个过程主要是将汇编代码转变成机器可以执行的指令
iv. 链接:将不同源文件产生的目标文件进行链接,从而形成一个可以执行的程序

15、递归和循环的区别
i. 递归时,每递归一层,就会在内存生成一个调用栈,来保存本次递归的信息,所以如果递归深度过深,就会有栈溢出的问题
ii. 循环是一次正向的过程,递归则需要回溯
iii. 在写法上,递归需要不断调用自身的函数,循环则不需要

16、i++ 和 ++i的区别
i. 首先 i++是先加后赋值而++i是先赋值后加
ii. 其次 前置返回一个引用,后置返回一个对象。而前置不会产生临时对象,后置必须产生临时对象,临时对象会导致效率降低。所以++i更快

17、const的四种作用
i. const修饰局部变量或全局变量,初始化后不能更改
ii. const修饰函数的参数,为了避免该参数被修改
iii. 用const修饰函数返回值,说明函数的返回类型是const的,则返回值不能被修改
iv. 用const修饰函数,则不能修改其成员变量的值

18、static的作用如果不考虑类的情况
i. 第一个作用是隐藏,不加static的全局变量和函数具有全局可见性,可以在其他文件中使用,而加了之后,只能在该文件所在的编译模块中使用
ii. 然后 作用在局部变量上,可以提高其生命周期。它不会出代码块而销毁,而是存储在静态存储区中
iii. 最后 用static修饰的变量,默认初始化为0考虑类的情况static修饰成员变量或者成员函数
它就只与类进行关联,而不再属于栈上某个对象的数据,所有对象都共享这一块静态存储空间


二、面向对象1、面向对象的三大特性
i. 封装封装是把客观事物封装成抽象类。比如把公共的数据或方法用public修饰,把私有的数据或方法用private修饰
ii. 继承继承是让某个类对象获得另一个类对象的属性和方法。
iii. 多态多态是指同一事物表现出不同的能力多态性是允许将子类类型的指针赋值给父类类型
实现多态的方式有两种:重写(运行时多态)和重载(编译时多态)

2、类的默认成员函数
i. 无参的构造函数:用于完成对象的初始化工作
ii. 拷贝构造函数:用于复制本类对象
iii. 赋值运算符重载函数:同样也是负责本类的对象
iv. 析构函数:用于对象的清理

3、C++ 类对象的初始化顺序,有多重继承情况下的顺序
i. 父类的构造函数优先初始化,若父类中包含成员类对象,则再初始化成员类对象,最后再初始化子类对象
ii. 成员变量的初始化与声明顺序有关
iii. 析构顺序与构造顺序相反
可以简单的理解为"套娃问题",子类包含父类的属性和方法,那么子类一定比父类"大",所以我们要先初始化"小"的,再从外面套上"大"的

4、简述一下 C++ 中的多态
子类重写父类的方法,然后用父类引用或指针指向子类对象,最后当我们调用方法时会进行动态绑定,这就是多态静态多态和动态多态区别静态多态:在编译期就已经确定,函数的重载就属于静态多态动态多态:在运行时才能确定,并且还需要完成动态绑定的条件
i. 父类中必须要有虚函数,然后子类重写父类虚函数
ii. 通过父类的指针引用来调用这个虚函数

5、虚函数和纯虚函数的异同

i. 首先 含有纯虚函数的类被称为抽象类而只含有虚函数的类不能被称为抽象类。
ii. 其次 虚函数可以被直接使用,也可以被子类重载以后,以多态的形式调用而而纯虚函数必须在子类中实现该函数才可以使用,因为纯虚函数在基类有声明而没有定义
iii. 最后 它们的定义形式也不同,虚函数是:virtual{},而纯虚函数是virtual{} = 0;
同:
i. 首先 虚函数和纯虚函数可以定义在同一个类
ii. 其次 虚函数和纯虚函数都可以在子类中被重载,以多态的形式被调用。
iii. 最后 在虚函数和纯虚函数的定义中不能有static标识符,因为被static修饰的函数在编译时就要进行绑定,然而它们却是动态绑定的

6、为什么析构函数一般写成虚函数
如果析构函数不被声明成虚函数,则编译器执行的是静态绑定,在删除父类指针时,只会调用父类的析构函数而不调用子类的析构函数,这样就会造成子类对象析构不完全造成内存泄漏C++默认的析构函数不写成虚函数,是因为虚函数需要额外的虚函数表和虚函数指针,会占用额外的内存空间。所以我们对于不会被继承的类来说,是不会设置为虚函数的

7、构造、析构函数是否可以为虚函数
构造函数不可以为虚函数因为 虚函数所对应的虚函数表的地址是存储在对象的内存空间中的,而此时对象都还没实例化,就没有空间让虚函数表存储地址。所以这里就会冲突析构函数可以为虚函数
我们可以将需要被继承的父类的析构函数设置为虚函数。当我们父类类型的指针绑定到子类对象时,能够保证释放父类指针的时候 可以同时释放掉子类的空间,防止内存泄漏


三、内存管理
1、什么是内存对齐
系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐。比如一个结构体中有一个int类型的数据和一个char类型的数据,它实际sizeof出来的大小占8个字节

2、为什么要进行内存对齐
为了提高数据读取的效率,程序分配的内存并不是连续存储的,而是按首地址为k的倍数的方式存储;这样就可以一次性读取数据,而不需要额外的操作结构体内成员按照声明顺序存储第一个成员地址和整个结构体地址相同
未特殊说明时,按结构体中size最大的成员对齐(若有double成员,按8字节对齐。)

3、栈和堆的区别
i. 首先 栈由操作系统自动分配释放,一般用于存放函数的参数,或局部变量堆由程序员通过new/delete关键字手动分配和释放
ii. 其次 栈使用的是一级缓存,通常是被调用时处于存储空间中,调用完毕立即释放而堆则存在二级缓存中,速度要慢一些
iii. 最后 栈的结果是先进先出,而堆是先进后出

4、内存模型
在C++中,内存分成5个区。分别是栈区、堆区、静态存储区、常量存储区、以及代码区
栈区:仅在定义的程序块运行时才存在。一般用于存储局部变量函数的形参
堆区:堆区的内存是通过new运算符动态分配的,也必须通过detele运算符手动销毁
静态存储区:内存在程序编译时就以及分配好,主要用于存放全局变量静态变量
常量存储区:这是一块比较特殊的存储区域,里面存放着常量。不允许进行修改
代码区:存放着程序的二进制代码

5、内存泄漏
简单地说就是申请了一块内存空间,使用完毕后没有释放掉比如:
i. new/malloc申请内存后,没有用delete/free释放
ii. 子类继承父类时,父类析构函数不是虚函数

6、避免内存泄漏
首先 可以用计数法,当我们使用new或者malloc的时候,计数就+1,而使用detele或free时,计数就-1。
最后打印这个计数,看看最后结果是否为0然后 一定要将父类的析构函数声明为虚函数最后要保证new/delete , malloc/free成对出现
解决方法:使用智能指针

7、new/delete 与 malloc/free的异同
相同点: 都可以用于内存的动态申请和释放
不同点:
i. 首先 new/delete 是C++中的运算符。而malloc/free是C/C++中的标准库函数
ii. 其次 new 在分配内存时会自动计算空间大小。而malloc需要手动计算
iii. 最后 new/delete 除了分配内存和回收内存以外,还具有调用构造函数和析构函数的功能。而malloc/free则只有会分配内存和回收内存

8、new和delete是如何实现的
new实现过程:
i. 先通过operator new()函数,它内部会调用malloc,在堆中分配一块内存
ii. 然后 将void类型的指针,转换为类类型的指针
iii. 最后再通过指针调用构造函数,用于初始化对象
delete实现过程:
i. 首先 调用析构函数删除内存中指针所指的数据
ii. 然后 通过operator delete()函数,它内部调用free去删除对象本身


四、STL
1、STL中迭代器的作用,有指针为何还要迭代器
i. 迭代器的作用用于指向顺序容器关联容器中的元素通过迭代器可以读取它指向的元素通过非const迭代器还可以修改其指向的元素
ii. 迭代器与指针的区别
迭代器是类模板,它只是表现的像指针。它模拟了指针的一些功能,重载了指针的一些操作符

2、vector 和 list的对比
vector:一维数组
特点:元素在内存连续存放,支持动态扩容,在堆中分配内存,元素连续存放,有保留内存,如果减少大小后内存也不会释放。
优点:和数组类似开辟一段连续的空间,并且支持随机访问,所以它的查找效率高其时间复杂度O(1)。
缺点:由于开辟一段连续的空间,所以插入删除会需要对数据进行移动比较麻烦,时间复杂度O(n),另外当空间不足时还需要进行扩容。
list:双向链表
特点:元素在堆中存放,每个元素都是存放在一块内存中,它的内存空间可以是不连续的,通过指针来进行数据的访问。
优点:底层实现是循环双链表,当对大量数据进行插入删除时,其时间复杂度O(1)。
缺点:底层没有连续的空间,只能通过指针来访问,所以查找数据需要遍历其时间复杂度O(n),没有提供[]操作符的重载。

3、map 和 unordered_map 的区别以及底层实现
map:内部实现了一个红黑树,红黑树有自动排序的功能,因此map内部所有元素都是有序的。红黑树的每一个节点都代表着map的一个元素。因此,对于map进行的查找、删除、添加等一系列的操作都相当于是对红黑树进行的操作。
map中的元素是按照二叉排序树的方式存储的,特点就是左子树上所有节点的键值都小于根节点的键值,右子树所有节点的键值都大于根节点的键值。使用中序遍历可将键值按照从小到大遍历出来
unordered_map:内部实现了一个哈希表,通过把关键值映射到Hash表中一个位置来访问记录,查找时间复杂度可达O(1)。因此它是无序的

4、请说说 STL 中常见的容器,并介绍一下实现原理
容器是可以用于存放各种类型数据的数据结构。可以分为顺序容器、关联式容器、容器适配器
三种类型顺序容器:容器中的元素没有进行排序,元素插入位置与元素的值无关。包含vector、list、deque
i. vector 的本质是动态数组。元素在内存连续存放。随机存取任何元素都能在常数时间完成。在尾端增删元素具有较佳的性能。
ii. list 的本质是双向链表。元素在内存不连续存放。在任何位置增删元素都能在常数时间完成。但不支持随机存取
iii. deque 的本质是双向队列。元素在内存连续存放。随机存取任何元素都能在常数时间完成(仅次于vector)。在两端增删元素具有较佳的性能(大部分情况下是常数时间)
关联式容器:元素是排序的;插入任何元素,都按相应的排序规则来确定其位置;在查找时具有非常好的性能;通常以平衡二叉树的方式实现。包含set、multiset、map、multimap(都是基于红黑树实现)
i. map 的底层容器是红黑树。map 的所有元素都是成对的,它的第一个元素被当成键,第二个元素被当成值。所有的元素都会根据元素的键值自动排序,且不允许键值重复
ii.set 的底层容器也是红黑树。但set中所有的元素只有键,没有值。不允许键重复,其中的元素会被自动排序,且不能通过迭代器来改变set的值,因为set的值就是键,set的迭代器是const的所以map和set的区别在于map的值不作为键,键和值是分开的容器适配器
封装了一些基本的容器,使之具备了新的函数功能,比如把deque封装一下,变为一个具有stack功能的数据结构

5、vector扩容机制,以及1.5倍扩容因子的优点
vector有保留内存,当减少vector的大小后,内存并不会释放;只有新增大小大于当前大小时,才会开辟新的内存空间
扩容机制:首先 开辟1.5倍的内存空间然后 将旧数据拷贝到新的内存再 释放旧内存最后 指向新内存


五、C++ 11特性
1、简述一下移动构造函数
移动构造函数实现的是对象值真实的转移。比如从A移动到B,这说明将分配给A的内存转移给了B。而不是新开一块内存给B

2、智能指针是否线程安全
智能指针包括一个实际数据指针和一个引用计数指针,这两个操作不是一个指令可以完成的,因此多线程环境下,是会有问题的同一个shared_ptr被多个线程读,是线程安全的;
同一个shared_ptr被多个线程写,不是线程安全的;

3、左值和右值的概念左值
左值指既能够出现在等号左边,也能出现在等号右边的变量它是可寻址的变量,有持久性
左值引用:引用一个对象(平时指的引用一般是左值引用)
右值右值则是只能出现在等号右边的变量一般是不可寻址的常量,或在表达式求值过程中创建的无名临时对象短暂性
右值引用:C++11中右值引用可以实现“移动语义”,通过 && 获得右值引用

4、智能指针的作用、原理、以及常用的智能指针
作用
让我们更方便的管理堆内存,若使用普通指针,则容易造成堆内存忘记释放、二次释放,程序发生异常时内存泄露等问题等。而使用智能指针能更好的管理堆内存
原理
智能指针是一个类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。将动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源
常用的智能指针
i. shared_ptr采用引用计数器的方法,允许多个智能指针指向同一个对象每当多一个指针指向该对象时,指向该对象的所有智能指针内部的引用计数加1,每当减少一个智能指针指向对象时,引用计数会减1当计数为0的时候会自动的释放动态分配的资源。
ii. unique_ptr采用的是独享所有权语义,一个非空的unique_ptr总是拥有它所指向的资源转移一个 unique_ptr将会把所有权全部从源指针转移给目标指针,源指针被置空;所以unique_ptr不支持普通的拷贝和赋值操作,不能用在STL标准容器中如果你拷贝一个unique_ptr,那么拷贝结束后,这两个unique_ptr都会指向相同的资源,造成 在结束时对同一内存指针多次释放而导致程序崩溃。
iii. weak_ptr首先 引用计数有一个问题就是互相引用形成环(环形引用),这样两个指针指向的内存都无法释放。所以需要使用weak_ptr打破环形引用。weak_ptr是一个弱引用,它是为了配合shared_ptr而引入 的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是说,它只引用,不计数。如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前使用函数lock()检查weak_ptr是否为空指针。
iv. auto_ptr(C++ 11弃用)主要是为了解决“有异常抛出时发生内存泄漏”的问题 。因为发生异常而无法正常释放内存但auto_ptr有拷贝语义,拷贝后源对象变得无效,这可能引发内存崩溃
而unique_ptr则无拷贝语义, 但提供了移动语义,避免这样的错误再次发生

5、C++ 智能指针实现引用计数的原理
i. 智能指针将一个计数器与类指向的对象相关联,引用计数器跟踪共有多少个类对象共享同一指针
ii. 每次创建类的新对象时,初始化指针并将引用计数置为1
iii. 当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数
iv. 对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则 删除对象),并增加右操作数所指对象的引用计数
v. 调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)

6、C++11 中四种类型转换
C++中四种类型转换分别为const_cast、static_cast、dynamic_cast、reinterpret_cast
const_cast:将const变量转为非const
static_cast:最常用,可以用于各种隐式转换,比如非const转const。还可以用于类向上转换,但向下转换能成功但是不安全
dynamic_cast:只能用于含有虚函数的类转换,用于类向上和向下转换reinterpret_cast:可以做任何类型的转换,但不保证转换结果是否正确


原创声明:文章版权为作者所有,未经允许,禁止转载,抄袭
发表于 2024-7-15 18:04 | 显示全部楼层


  • 熟悉项目

    • 准备阶段最重要的一点就是熟悉自己的项目,对自己的项目熟悉程度,一定要熟悉到每个细节,因为面试官很大程度上会提问与项目相关的问题

  • 基础

    • 这里的基础我指的是C#基础,我个人认为,C#基础的是比较重要的,尤其是一些底层的问题。面试官很喜欢问这部分,如果你懂底层的话,是可以加分的
    • 如果能够详细地说清楚这些内容,我想面试官也会高看一眼,说明我们不是一个只会依赖现有组件的一个初学者,而是能够去钻研底层问题的

  • Unity相关组件的使用

    • 一般面试官都问到这部分问题,也会结合实际应用场景来提问,其实主要考验的就是对组件的熟悉程度,以及如何去使用这些组件去解决实际

  • 算法

    • 除了Unity组件这一部分,我也去看了算法相关的一些知识点,主要还是游戏相关算法。比如选中算法,数组相关的问题,因为游戏当中用到的很多数据都是用数组或者是列表来存储
    • 目前来看,面试中考查到的算法部分都不算很难。但我还是建议去熟悉

  • 公司背调

    • 我给所有我发过简历的公司都做了简单的背调
    • 包括公司人数、它的规模、正在做的产品等等信息,然后根据自己喜好,还有对他们公司的满意度,做了简单的评级
    • 当然这些统计里是有强烈的个人主观因素在里面的,因为对我个人来说,我不太想出省,所以我找的公司都是省内的公司,集中在珠海、深圳、广州这三个城市,所以就仅仅作为参考。具体情况还是根据大家的实际情况来分析

  • 小公司练手

    • 在我给自己感兴趣的公司投简历之前,我是给一些相对来说较小的公司投了一下,不一定会去,主要还是练练手,熟悉一下面试流程,积累一下面试题目
    • 因为各大公司的面试题目都是有一些重复的地方或相似的地方,遇到有不会的面试题目还可以结束之后找资料学习

详细内容可以参考我们的《创客说V01:零经验U3D学员面试访谈》公开课

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-21 20:32 , Processed in 0.113208 second(s), 28 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表