日常实习C++面经汇总与教程
最近忙了大概一个月,终于找到实习了,这里把自己面试过程遇到的问题记录一下。
ps:很多东西我之前都写过文章,所以重复的地方就直接贴上过去的文章的链接了。
这里只放上有关C++的内容,其它的例如项目啊,编译链接工具的使用就因人而异了。
C++中的多态
以下是一个常见的多态的场景
struct A { virtual void foo() = 0; }; struct B : A { virtual void foo() { cout << "this is B\n"; } }; struct C : A { virtual void foo() { cout << "this is C\n"; } }; struct D : A { virtual void foo() { cout << "this is D\n"; } }; int main() { vector<A*>v = { new B{},new C{},new D{} }; for (auto& it : v) { it->foo(); } }
这里我们用到了虚函数
对于C++中对象的内存模型,我的建议是用C语言模拟一个
这里一个经常遇到的问题,为什么我们说虚函数慢?
考虑到,函数在执行的时候,需要进行跳转(汇编的角度而言就是动cs:ip
而虚函数,需要先去虚函数表里面,然后在跳转到相应的函数上,也就是跳转的次数多了,对cache不友好。
但是,最重要的是,虚函数不能进行内联优化。
请记住,C++的编译器的优化水平很高,但是对于虚函数这种“运行期”确定的函数,是不能依靠编译器进行编译优化的。
这里提到了,运行期,和编译期。这两个概念在后面也会提到。
那么如何解决这个问题呢?
我们可以使用编译期多态来CRTP来解决。
(CRTP不是万能的,有些东西必须是运行期的
智能指针
智能指针的实现就是RAII
现代C++如果非要说是C with xxx的话,那么就是 C with RAII
至于智能指针的具体实现,网上有很多,大家自己去看就可以了。
谈及智能指针,最好也谈以下所有权的概念
例如,我现在有个资源,然后我有个函数,我是选择把所有权给这个函数啊,还是把所有权留在原来的作用域。
(这个内容在上面的文章就有提及。
模板
模板就是面向编译器的编程,我们通过编写模板相关的代码,让编译器帮我们根据提供的类型,生成对应的代码。
另外,C++的类型系统是十分强大的,所以有关模板的内容希望大家好好学习。
C++模板学习实践 (这是个专栏,内容可能比较多,看是看完肯定有收获的
另外,有关模板的发展(可以我的文章里面的模板元都是const static 这种比较old的。
首先是模板变量和模板类型,过去我们需要::value,::type 现在可以直接定义了。
对于标准库,_v 就是 ::value , _t 就是::type ( 所以学一学old版本的很有帮忙的
还有就是constexpr 函数,对于编译期计算,可以直接constexpr 函数解决。
另外就是C++20的概念与约束了
过去我们需要用 严格鸽:现代C++学习——模板SFINAE 来表示一个鸭子类型。
鸭子类型:我希望一个类型具有这样的变量,这样的函数。。。
例如下面我们就约束了类型T是一个int或者一个具有size函数len成员变量的类型。
template<typename T> concept is_int = is_same_v<T, int>; template<typename T> concept need = requires(T x) { x.size(); x.len; }; template<typename T> requires(is_int<T> || need<T>) void foo(T x) { } int main() { foo(1); struct A {void size();int len;}; foo(A{}); struct B { void Size(); int Len; }; foo(B{});//编译失败,ide会报错 }
上面是几个经常被问道的内容,属于基础了,下面放上几个杂项。
lambda
严格鸽:c++函数进化史 (函数,函数指针,function,仿函数,lambda)
lambda编译器帮我们生成的匿名的仿函数
variant
variant是可以析构的union
严格鸽:C/C++ union 使用教程 (常见操作与缺陷)
严格鸽:现代C++学习——实现多类型存储std::variant
类型擦除
在问std::any的实现的时候问的。
在C语言总,一般是用void* ,然后自己维护一个type
模板元编程
就让写了个斐波那契数列
但是你可以归并排序!
多线程
问了一些实现什么的
原子类型是用cas (compare and swap) 实现的。
其它的锁都是基于原子类型的操作
std::call_once
之前没有听说过
std::call_once - cppreference.com
pimpl
为了加快编译时间的,但是我没这么用过(编译时间不是用来摸鱼的吗
有个面试官说过,可以用编译参数来控制。
堆,栈空间
堆是os帮我们维护的,栈空间编译器帮我们维护了。
注意不要写出这样的代码
int* foo() { int arr[100]; return arr; }
异常安全
没有怎么用过(最多几千行的代码,用啥异常啊)
可以了解下std::optional 与 std::expected
关于noexcept,可以看下这个代码
void foo1() noexcept; void foo1() noexcept(noexcept(foo1())) { } void foo2(); void foo2() noexcept(noexcept(foo2())) { } int main() { constexpr bool x = noexcept(foo1()); constexpr bool y = noexcept(foo2()); }
有一些忘了
#我的实习求职记录#