c++常见八股
c++基础
1.c++中的左值和右值
c++左值一般值得是能够&的变量,他们是有名字的,占据一部分内存的变量,c++的右值一般是没有名字,在这一行代码写完后就立刻销毁或者不存在的值。 我们可以用move函数将一个左值转化为右值
c++新特性
c++智能指针
c++左值和右值
总思路:
为了减少深拷贝带来的内存 / 性能浪费,我们需要实现「移动语义」(掠夺临时对象的资源而非拷贝);但要让编译器知道 “什么时候该拷贝、什么时候该移动”,就需要一套清晰的判别标准 ,而这套标准就是左值和右值。


1.什么是左值,什么是右值?
简单的说左值就是赋值运算符左边的值而右值就是赋值运算符右边的符号。 更进一步的说,左值是在程序运行过程中持久化的对象,需要内存来存储他们的数据,比如一个简单的变量。而右值并不需要,他们往往创建后会立刻销毁只短时间占用内存空间的值,比如运算的中间值等。
2.什么是左值引用什么是右值引用?
&a 表示左值引用,它往往出现在函数形参中,它可以让函数被调用的时候不再拷贝一份新的对象,而是像操作原本的值一样操作函数内的值。其中它的本质是一个指针常量,这个指针指向的方向无法更改。引用只需要创建一个指针常量指向这个对象,每次当访问这个别名的时候编译器都偷偷解引用。这样在代码层面就好像起了一个别名一样。
最后提一下万能引用: 像这样类型需要推导(传递),并且参数 T&& 像是这样的引用叫万能引用
template<class F>
void AddTask(F&& task) {
{
std::lock_guard<std::mutex> locker(pool_->mtx);
//forward函数为了保持原本的task的左值和右值,传入进来的是左值就是左值,是右值就是右值
pool_->tasks.emplace(std::forward<F>(task));
}
//唤醒wait函数
pool_->cond.notify_one();
}
&&a表示右值引用 同左值引用的底层一样,只不过这个指针常量指向的是一个右值,仅此而已。
3.左值和右值主要是为了区分和拷贝语义和移动语义
拷贝语义(深拷贝)
就是有一个新对象,这个新对象的所有属性都从旧对象拷贝一份过来。原对象其实还是有所有权的
移动语义(浅拷贝)
它的核心是拷贝一个新的对象出来,这个新的对象继承了原来对象的所有权。 如果是简单的int和char类型这种会是简单的值传递,如果是指针,那么新对象的指针就指向原本的区域,并且将旧对象的指针置空。 如果是其他类型的对象,会递归的调用它的移动构造函数来实现整体的移动拷贝。核心就是旧对象对资源的所有权转移给新对象。 这个所有权一般指的是对象所拥有的内存,或者某些fd。
4.将一下move函数和forward函数
其中move函数是将一个左值转化为右值。
forward叫完美转发,它不仅转发了函数参数本身,连这个参数是左值还是右值也转发了。
c++互斥量和条件变量
c++异步任务
c++类与对象
1.介绍一下面相对象的三大特性
答:
1.封装
2.继承
3.多态
多态分为静态多态和动态多态。
静态多态:常见的有函数重载和模板
函数重载简单说,在函数参数类型和个数不同的情况下,一个函数名可以用许多次。
模板:可以按照传入的参数类型调用不同的方法。
缺点是比较死板,必须提前知道参数的类型和个数或者提前知道模板被什么样的类型使用。
动态多态
通过虚函数和继承来根据不同的对象来调用不同子类重写父类的虚函数。适用于用同一个规范,但是要按照不同的对象类型来调用不同方法的情况。
其中虚函数的开销是1.虚函数表 2.虚函数表指针 3.动态的绑定过程
1.虚函数表:有虚函数或重写了虚函数的类会创建一个虚函数表,里面存储了所有该类虚函数的地址。
2.虚函数指针: 这个指针指向了该对象所对应的虚函数表,在每个对象的前8个字节存储。
3.动态绑定的过程:
动态绑定过程: 如:当用Animal* a = new Dog()时: 创建Dog对象时,会设置a->vptr指向Dog类的vtable。
当调用a->speak(),程序会通过vptr找到Dog类的vtable,调用里边的speak()地址。
c++网络编程
1.请解释ET(edge-triggered)和LT(level-triggered)
LT(Level-Triggered,水平触发)
工作方式:当文件描述符处于就绪状态时,系统会持续通知你。
-
特点:收到数据后,没读完之前会反复发送通知
-
举例:
-
假设 socket 接收缓冲区有数据可读(>0 字节)
-
操作系统会一直通知你"有数据可读"
-
即使你只读取了一部分数据,只要缓冲区还有数据,下次调用
epoll_wait()时还会收到通知
-
ET(Edge-Triggered,边缘触发)
工作方式:只在文件描述符状态发生变化时通知一次。
-
特点:只在状态变化的"边缘"触发通知
-
举例:
-
当 socket 从"无数据"变为"有数据"时,只通知一次
-
如果你没有一次性读取完所有数据,剩余的数据不会触发新通知
-
直到有新的数据到达(状态再次变化),才会再次通知
-
2.请你说一下为什么LT可以支持阻塞io,而ET不行呢?
阻塞IO:
这里阻塞io指的是如果要读这个数据,程序会一直等待,就算没有数据,程序也不会做其他事。非阻塞io是他会正常读取,如果此时没有数据读,他会返回false。这样就提高了cpu的利用率。
答:
ET是必须一次性将数据读完的,因为epoll只给他发一次信号。
为了ET一次性读完,那么他的处理函数必须写成一个循环。
但是由于是阻塞io,当读取到最后一次,没有数据的时候程序会阻塞在原地无法运行。所以必须用非阻塞io解决这个问题。因为非阻塞io提供了一种错误码errno = eagain,我们可以通过调用非阻塞的read函数对错误码进行判断是否读取完毕。这样就解决了死锁问题,可以安全退出一个循环。所以ET模式必须采用非阻塞。
3.那么LT和ET都适合在什么情况下使用呢?
LT是默认状况:
1.收发的数据比较大,难以一次读取完成
2.追求简单代码,容易实现,并且不容易出错
ET是比较激进的情况,他比较适合需要高并发和吞吐量大的场景,属于压榨性能的一种设计。
实习问到的补足
1.什么是指针函数,什么是函数指针?他们有什么区别?
2.编译的过程有?
答:预处理,编译,汇编,链接
3.位运算记得刷题
3.1无符号整数和int之间的相加减,最后的值问题。
4.一个64位的操作系统,指针占几个字节?
答:8个字节 64/8
更多推荐



所有评论(0)