Graphics Software面试准备

个人针对芯片公司的Graphics Software岗位的面试笔记,主要分为4部分:c++基础图形学基础图形API

c++基础

此部分综合了网络上的c++面经、笔记以及个人在面试中遇到的问题。

Part1:class相关

  1. 纯虚函数可以有实现吗?
    答:语法层面来讲可以有,但是一个类只要含有纯虚函数就变成了抽象类,为纯虚函数提供实现也无法实例化。纯虚函数由继承该类的子类实现,子类可以实例化。
  2. 定义一个空类,有哪些默认的函数?
    默认构造函数
    默认拷贝构造函数
    默认析构函数
    默认重载赋值运算符函数
    默认重载取址运算符函数
    默认重载取址运算符const函数
    默认移动构造函数(C++11)
    默认重载移动赋值操作符函数(C++11)。
  3. 析构函数为什么要设置成虚函数?构造函数可以是虚函数吗?
    构造函数不能是虚函数,编译会报错。每个对象的虚函数表是在构造函数中初始化的,因此构造函数不能是虚函数。析构函数一般都是虚函数,当一个基类指针指派生类对象时,通过该指针调用对象时就可以调用到子类的析构函数,从而释放所有资源。
  4. 虚函数的实现原理?
    虚函数通过虚函数表实现,有以下几个注意点:
    1)虚函数表是编译器在编译时期为我们创建好的, 只存在一份。
    2)定义类对象时, 编译器自动将类对象的__vfptr指向这个虚函数表。 3)vfptr是指向函数指针数组的指针,const void**,指针指向的内容不可更改。
    4)如果派生类实现了基类的某个虚函数,则在虚表中覆盖原本基类的那个虚函数指针,在编译时根据类的声明创建。 5)编译器生成虚表-(指针数组)》开辟内存-》写入虚表(吧虚表写入到内存中)-》构造函数-》调用虚函数。 6)构造函数内部调用虚函数,语法层面是允许的,但调用虚函数只能调用该类的虚函数,无法实现多态。
  5. 内联函数可以是虚函数吗?
    虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。内联是在编译期建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。
  6. 为什么类的成员模版函数不能是虚函数?
    因为模板函数可以有无数个实例,所以编译器在编译时无法为其生成一个确定的虚函数表条目,无法确定需要调用哪个特定的模板实例。
  7. 隐藏是什么?
    派生类的函数屏蔽了基类的同名函数,只需要函数名字相同,无论参数列表是否相同,其基类函数都会被隐藏。
  8. 空类的大小是?
    1字节。要求每个对象必须具有独一无二的内存地址。
  9. 在成员函数中调用delete this会发生什么
    在类对象的内存空间中,只有数据成员和虚函数表指针,并不包含代码内容,类的成员函数单独放在代码段中。当调用delete this时,类对象的内存空间被释放。在delete this之后进行的其他任何函数调用,只要不涉及到this指针的内容,都能够正常运行。对象不可以使用。一旦涉及到this指针,如操作数据成员,调用虚函数等,就会出现不可预期的问题。
  10. 在类的析构函数中调用delete this会发生什么?
    栈溢出。delete关键字会调用类的析构函数,从而造成栈溢出。
  11. 为什么成员初始化列表会快一些?
    对于类型,它少了一次调用构造函数的过程,而在函数体中赋值则会多一次调用。而对于内置数据类型则没有差别。

Part2:语言基础

  1. 引用的本质是什么?引用和指针的区别是什么?
    可以将引用理解为一个对象的别名,引用在底层是通过指针实现的,const ptr*。引用在创建时必须被初始化,不能为NULL,在初始化之后不能进行更改。

  2. volatile关键字
    volatile 关键字声明的变量,每次访问时都必须从内存中取出值,告诉编译器不应对这样的对象进行优化。const 可以是 volatile (如只读的状态寄存器)。

  3. sizeof
    数组作为参数传入函数后会退化为指针,sizeof的结果是指针的大小。当数组直接作为 sizeof 的参数时,返回的是数组占据内存空间的大小。

  4. c++字节序列
    大端:高位字节在低地址,低位字节在高地址。
    小端:低位低地址,高位高地址

  5. 左值和右值
    左值是可以取地址的,而右值不可以。
    右值引用和move语句结合使用可以避免拷贝。

  6. 引用折叠
    T& & -> T&
    T& && -> T&
    T&& & -> T&
    T&& && -> T&&
    万能引用

template <typename T>
void MyFunc(T&& value) {
}
  1. c++内存模型
    1)堆
    2)栈
    3)自由存储区 new/delete
    4)常量存储区
    5)全局/静态存储区

  2. typedef vs #define

  • 用法不同:typedef 用来定义一种数据类型的别名,增强程序的可读性。define 主要用来定义 常量,以及书写复杂使用频繁的宏。
  • 执行时间不同:typedef 是编译过程的一部分,有类型检查的功能。define 是宏定义,是预编译的部分,其发生在编译之前,只是简单的进行字符串的替换,不进行类型的检查。
  • 作用域不同:typedef 有作用域限定。define 不受作用域约束,只要是在define 声明后的引用 都是正确的。
  • 对指针的操作不同:typedef 和define 定义的指针时有很大的区别。
  1. 静态链接库和动态链接库
  • 在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
  • 动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。

STL相关

  1. std::vector
    1)vector底层通过动态数组实现,空间不够时会进行扩容,将空间扩大为原来的1.5倍,将原有数据拷贝到新的空间中,释放原来占据的空间。
    2)reserve和resize:reserve只改变capacity,但是不改变size。resize(n)将size变为n,若原有的capacity<n,则改变capacity。
    3)迭代器:插入和删除元素都可能导致迭代器失效(开辟新的空间,导致原地址失效)。使用迭代器删除元素时,it = vec.erase(it);

  2. 红黑树和AVL树
    stl中的map和set都是基于红黑树实现,每个节点是红色或者黑色,根结点和叶子结点都是黑的,如果一个节点是红色的,那么它的两个子节点都是黑色的。
    AVL树是一棵二叉搜索树,每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。
    AVL 树比红黑树更加平衡,但AVL树在插入和删除的时候也会存在大量的旋转操作。所以当你的应用涉及到频繁的插入和删除操作,切记放弃AVL树,选择性能更好的红黑树;当然,如果你的应用中涉及的插入和删除操作并不频繁,而是查找操作相对更频繁,那么就优先选择 AVL 树进行实现。

图形学基础

芯片公司的graphics software岗位要求对图形学相关的基础知识有一定了解。下面总结一些常见的图形学问题。

  1. 渲染管线
  2. ShadowMap原理、常见的问题以及解决方案
  3. PBR
  4. Anti-Aliasing
  5. Culling
    更多内容可参考图形学面经,总结的很全面。

图形API相关

本人主要学习了Vulkan API,在这里总结一下常见的面试问题。

  1. Vulkan画一个三角形需要哪些步骤?
    考察Vulkan中的基础概念。
  • VkInstance
  • VkDevice
  • VkQueue
  • VkSwapchain
  • VkCommandPool & VkCommandbuffer
  • VkRenderPass & VkFramebuffer
  • VkPipeline & VkPiplineLayout & PSO & VkDescriptorSet VkDescriptorSetLayout VkDescriptorPool
  • VkSemaphore VkFence Barrier
  1. Vulkan中的多线程渲染
    Secondary commandbuffer, 多线程
  2. Vulkan PSO
    构建Pipeline需要Pipeline State Objects,包括fixed function的设置、shader设置、pipelineLayout设置,重复利用PSO可以提升性能。
  3. Vulkan Performance Optimization
  4. Variable rate shading
  5. RayTracing API
#面经##图形驱动#
全部评论
可以推荐一些针对这些知识的公司吗
点赞 回复 分享
发布于 09-29 17:05 湖北
m
点赞 回复 分享
发布于 10-25 14:43 北京

相关推荐

9 26 评论
分享
牛客网
牛客企业服务