C++、图形学面经总结

这几天在实习面试,看了很多别人的面经,对相关问题总结了一下。

C++

  • 定义一个struct,有int x,char c两个成员,这个结构体的大小?如果增加一个static int 这个结构体大小怎么变化?
    由于内存对齐大小为8,不变,static存储在静态区.

  • 为什么要内存对齐?
    由于内存的物理结构,硬件取指的方便,程序移植性的需要.

  • inline与宏区别?inline函数与普通函数区别?
    内联函数在编译时展开,宏在预编译时展开;内联函数直接嵌入到目标代码中,宏是简单的做文本替换;内联函数有是函数,有类型检测、语法判断等功能,而宏没有.
    普通函数调用有标准函数调用开销,inline内联函数在编译过程中,没有函数开销,在函数调用点直接将函数代码展开;inline函数不会再生成相应的函数符号;

  • 什么时候会发生浅拷贝?深拷贝是什么?作用?怎么实现?
    值传递时:对象作为函数参数时,返回对象时,赋值给另一个对象时.对类的动态成员重新分配空间.避免指针悬挂.为需要复制的动态成员开辟空间并赋值.

  • 析构函数为什么一般情况下要声明为虚函数?
    当父类指针指向子类对象时,析构对象时能够调用子类的析构函数,否则只会调用父类的析构函数.

  • 强制类型转换的种类、区别?
    reinterpret_cast 主要用于转换不相容的数据类型,特别是能够把任何数据类型转换为指针类型和引用类型,以及相反的过程,也可以用于指针之间的转换。 错误使用reinterpret_cast操作符很容易产生不安全的行为
    static_cast 基类和子类之间的转换:子类指针转换成父类是指针是安全的;但父类指针转换成子类指针是不安全的;基本数据类型转换,如enum,struct,int,char,float等;空指针转换成目标类型的指针;任何类型的表达式转换成void类型;Static_cast不能去掉类型的const,volitale属性
    dynamic_cast dynamic_cast会进行运行时检查。当对于一个模糊指针使用static_cast时不会报错,使用dynamic_cast时会报错。 dynamic_cast仅仅对指针和引用有效,并且允许时检查也会影响效率。
    const_cast 去掉类型的const或volalitle属性;把一个本来不是const类型的数据转换成const类型;const_cast操作不能在不同的种类间转换

  • volatile关键字的作用?
    提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。

  • 模板声明与实现如何能不在同一个头文件?
    include实现的cpp文件;在实现时使用export关键字,include头文件.

  • 类中静态变量的初始化是什么时候?
    在类外显式地初始化.

  • 左值与右值?左值引用和右值引用?
    左值是在内存的表达式,能够用内置的&进行取地址,其它可以算右值,右值更多的是一种“值”的表达,其中广义上右值又包含了纯右值与亡值,亡值一般就是我们说的生命周期即将结束的表达式.简单来说,在内存中有别名指代的就是左值,而指代的内容和各种匿名内容是右值.
    左值引用是左值的别名,右值引用是右值的引用,能延长右值的生命周期

  • 移动语义和万能引用和完美转发?
    带资源的对象需要拷贝时,想要有浅拷贝的效率,还想要深拷贝析构时安全的效果,那么将被拷贝者资源权转给拷贝者,自己不再使用,来保证安全和高效。
    函数传进来的参是左引用,函数体也是左引用,参是右引用,函数体内也是右引用(不需要为左右值的参数各重载一遍了)时就是万能引用.万能限定必须是函数模板,可以模板参数是单个,也可以是多个模板参数,形式为T&&.
    std::forward()能够保持原本的引用关系

  • move的作用?
    无条件将参数转化为右值,减少资源创建拷贝和释放,将对象状态和所有权从一个对象转移到了另一个对象.

  • 什么是引用折叠?
    多重引用会被折叠成左值或右值引用,多余的忽略.

  • 类中默认构造函数有哪几种?
    初始化构造函数,复制构造函数,移动构造函数,继承构造函数,委托构造函数,类型转换构造函数

  • 拷贝构造函数和移动构造函数的区别?
    拷贝时触发和移动时触发,浅拷贝和移动深拷贝.

  • static和const
    const数据成员 只在某个对象生存期内是常量,而对于整个类而言却是可变的。const定义的常量在超出其作用域之后其空间会被释放.const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数.
    static定义的静态常量在函数执行后不会释放其存储空间。即使没有具体对象,也能调用类的静态成员函数和成员变量。static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部.
    const成员函数主要目的是防止成员函数修改对象的内容。
    static成员函数主要目的是作为类作用域的全局函数。不能访问类的非静态数据成员。

  • sizeof类的大小?
    class A{}; sizeof(A())空类的大小为1. class B:A{int a;};sizeof(B())空类的子类对象大小为4(由于空基类优化).
    带静态成员的类,静态成员存放在类对象以外.
    带非虚函数成员的类,非虚函数成员类被存放在类对象以外.
    带虚函数的类,class A{virtual void func();};sizeof(A())大小为8.带虚函数成员的对象会生成一个指向该类虚函数表的指针.基类带虚函数,派生类也会带有8字节大小的指针.
    类也遵守内存对齐规则.
    普通派生类,派生类会存放基类中非静态数据成员的副本.
    多重继承下的内存对齐不会压缩,依然对齐.

  • 指针和引用的区别?
    指针是变量,引用是别名.指针可以为空而且可以改变,引用定义时必须初始化且不会改变.可以有const指针,不能有const引用.

  • 无效引用?
    1.引用到了未初始化的指针,指针可能指向任意位置.2.函数返回值是局部对象的引用

  • 调用函数的过程?
    在堆栈中变量分布是从高地址到低地址分布,EBP是指向栈底的指针,在过程调用中不变,又称为帧指针。ESP指向栈顶,程序执行时移动,ESP减小分配空间,ESP增大释放空间,ESP又称为栈指针。
    在每个作用域中,EBP不变,ESP随参数入栈而减少,

  • extern关键字
    extern关键字放在变量或函数前,表示这个变量或函数的定义在其他文件中,提示编译器遇到这个变量或函数时在其他模块中寻找定义.同时可以当作链接指定,按照C的规则编译.

  • vector底层,扩容原理
    底层是一段连续的
    线性内存空间,使用三个迭代器表示,分别是指向容器起始对象的、指向容器末尾的、指向当前最后一个元素波末尾的。
    扩容原理:将当前空间完全弃用,重新申请更大的内存空间,将旧元素移动到新空间中,将旧空间释放。

  • deque底层原理
    deque的元素不是线性存储的,而是存储在不同的连续空间段中,deque维护一个数组称为map保存了所有段的头指针。deque内部有start和end两个deque迭代器。deque迭代器维护了4个指针,分别是指向当前段的首地址、指向当前段的尾地址、指向当前元素、指向map中存储本段元素的地址。
    deque的start指针cur是从段尾开始,方便向前插入;end指针cur从段首开始,方便向后插入。

  • 哈希冲突
    处理哈希冲突的方法:开放地址法(当key的哈希地址p(key)冲突后,再次以哈希地址为基础进行哈希(p(p(key))),直到找到不冲突的地址),链表法(在冲突的位置建立链表)

  • 写出一个乘法的宏定义
    #define x(a,b) ((a)*(b))

  • 讲一下红黑树
    每个节点都是红色或黑色,根节点是黑色、叶节点也是黑色,没有两个相邻的红色节点(每个红节点的两个子节点都是黑色的)、任意节点到叶节点的路径包含相同数量的黑色节点。
    红黑树在插入和删除节点时都会进行变色和旋转操作,保证整体为二叉搜索树。插入删除搜索操作的复杂度为O(log2N)

  • 智能指针
    原因:堆上申请内存后需要手动对内存进行释放,容易造成内存泄漏。
    智能指针能够帮助管理堆上的内存,自动释放内存空间。
    unique_ptr:单独占有对象的所有权,不能被复制,只能被移动所有权。内部数据是原生指针和析构器
    shared_ptr:多个shared_ptr可以拥有同一个原生指针的所有权,使用引用计数的方法管理指针,引用计数为0的时候释放拥有的原生指针。问题是会出现循环引用。内部实现是原生指针以及管理引用计数和析构器的控制块。控制块的拷贝是浅拷贝所以能够共享计数。
    weak_ptr:weak_ptr不会增加引用计数,可以避免内存泄漏,不能直接调用原生指针方法,需要先转换成shared_ptr才能使用原生指针的方法。原生指针和控制块,但是没有重载->不能调用原生指针中的方法。
    注意事项:使用智能指针后不要再使用原生指针,不要把一个原生指针交给多个智能指针管理,不能管理栈上对象,不要将this指针托管给智能指针

  • 虚函数相关
    C++多态分为静态多态(编译阶段,函数重载和泛型)和动态多态(运行阶段,虚函数:根据调用的类型调用相应函数)。父类指针指向子类对象,调用父类中声明的虚函数,会执行子类重写的函数。
    构造函数不能是虚函数,析构函数可以是虚函数。父类虚构函数不声明为虚函数的话,只会调用父类的析构函数,不调用子类析构函数;父类声明虚析构函数,先析构子类,再按继承倒序析构父类。
    虚函数表在编译期间建立,虚表本质是指针数组,不会保存非虚函数指针,虚函数指针在运行阶段确定,每个实例都保存有指向虚函数表的指针(一个父类一个指针,父类有虚函数时,父类子类都有虚表指针,子类有虚函数时,父类有虚表指针),虚函数表和类绑定。每个父类都有自己的虚表,存放父类中的所有函数,子类的函数放在父类函数后,多继承时子类函数只放在第一个父类的表后。当子类重写了父类的虚函数,子类的重写的函数就替代了父类原有的函数位置。

  • final关键字
    用在类后,表示无法被继承;用在方法后,表示这个方法无法被子类重写。

  • template
    模板参数是类型时,不给默认类型的话,可以做隐性类型推断.但是,模板参数是其余类型时,必须显示指定或者给出默认值. 正常定义模板参数时,typename和class关键字都可以,但是涉及到从属类型时,必须用typename,告知编译器这是个从属类型而不是静态变量.从属类型:内部类或内部typedef.

  • constexpr
    常量表达式允许一些计算发生在编译时,使用constexpr可以创建编译时期的函数,这个函数在编译时计算出需要的数值.
    constexpr函数的限制:函数中只能有一个return语句,只能调用其它constexpr函数,只能使用全局constexpr变量.constexpr函数也可以运行时被调用.

  • if constexpr

  • decltype和auto
    decltype()进行变量类型推断,返回变量的类型;auto推断类型.区别:1.auto忽略顶层const,decltype保留顶层const;2.对引用操作,auto推断出原有类型,decltype推断出引用;3.对解引用操作,auto推断出原有类型,decltype推断出引用;4.auto推断时会实际执行,decltype不会执行,只做分析。
    decltype()推断出的类型与源类型完全一致.decltype(())表示引用.

  • using
    1.用于声明命名空间 2.用于替代typedef 3.继承构造函数的语义

  • 函数指针 lambda
    函数签名可以当作函数的参数.函数签名可以当作模板参数.lambda函数当做函数返回值时,参数传递不能用引用传递.
    默认lambda表达式获得的是个函数,它的大小是通过值传递捕获的局部变量的大小总和.

auto m(int i){
    int a = 1;
    return [=](int n){return n*i+a;}; // 这里返回值大小为8,因为它捕获有i和a两个值传递的局部变量.这里lambda使用引用传递会报错.
}
auto f = m(1);
cout<<sizeof(f)<<endl; // 大小是8
cout<<typeid(decltype(f)).name()<<endl; // 输出f的类型,是m(int)::$_0,说明f的类型是函数,函数会记录捕获的局部变量的大小,所以是8

auto mp = [](){}; 
cout<<sizeof(mp)<<endl; // 大小是1

函数指针的大小永远是8.
通过std::function<>定义的函数大小为32字节, 哪怕是std::function<>类型的lambda函数,不管它用值传递捕获了多少变量,函数大小都是32.
lambda表达式递归:

int main(){
    vector<int> arr = {1, 4, 2, 8, 5, 7, 1, 4};
    set<int> visited;
    auto dfs = [&] (auto const &dfs, int index) -> void {
        if (visited.find(index) == visited.end()) {
            visited.insert(index);
            cout << index << endl;
            int next = arr[index];
            dfs(dfs, next);
        }
    };
    dfs(dfs, 0);
    return 0;
}

完美引擎岗面经

C++ : 讲讲stl中的hashmap,哈希冲突,性能瓶颈,红黑树
图形:主要是项目中的内容,阴影 光照的实现,pbr,管线中不同阶段的变换矩阵 裁剪空间的xyzw范围

祖龙图形渲染岗面经

全程高强度图形学:球谐函数是什么、IBL原理,diffuse物体怎么通过球谐函数求光照,PBR、为什么漫反射要除\pi、推导一下,shadowmap想要更大的范围该调整什么,MVP矩阵都在干什么,w的含义,法线空间、TBN矩阵,裁剪空间xyz的范围,mipmap怎么求层级,为什么要reverse-z,为什么要透视除法,bloom原理,怎么实现颜色分级、色调映射,射线和球求交点,两个直线求距离
然后问了问有什么学习建议:看RTR4、关注一下具体的图形API、看UE4代码的实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容