网易面试

内联函数是否能是虚函数

参考:https://blog.csdn.net/fightHHA/article/details/81772399
内联函数能否声明为虚函数
(1)只有成员函数才能声明为虚函数,因为虚函数仅适用于有继承关系的类对象,所以普通函数不能声明为虚函数;

(2)虚函数必须是非静态成员函数因为静态成员函数不受限与某个对象;

(3)内联函数不能声明为虚函数,因为内联函数不能再运行中动态确定其位置;

(4)构造函数不能声明为虚函数,多态是指不同对象对同一消息有不同的行为特征,虚函数作为运行过程中多态的基础,主要是针对对象的,而构造函数是在对象产生之前运行的,因此,虚构造函数是没有意义的。

类中定义的成员函数(函数体在类中)能成为虚函数,大部分编译器能够将虽然声明为inline但实际上不能inline的函数自动改为不inline的。至于编译器会不会将inline and virtual的函数照模照样的实现,与编译器及优化方式有关。
要想成为虚函数,必须能够被取到地址.内联函数不能被取到地址所以不能成为虚函数.

你写inline virtual void f(),不能保证函数f()一定是内联的,只能保证f()是虚函数(从而保证此函数一定不是内联函数)

对于问题:
到底内联函数能不能成为虚函数?
答案是不能.问题是你不能够确定一个函数到底是不是inline的.inlien关键字只是对编译器的一个建议:"如果有可能,请把此函数变成inline的"

构造函数为什么不是虚函数

参考:https://cloud.tencent.com/developer/article/1036059
1、从使用角度
虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。
虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。
2、从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数
从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数

当一个构造函数被调用时,它做的首要的事情之一是初始化它的V P T R。因此,它只能知道它是“当前”类的,而完全忽视这个对象后面是否还有继承者。当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码- -既不是为基类,也不是为它的派生类(因为类不知道谁继承它)。
所以它使用的V P T R必须是对于这个类的V TA B L E。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内, V P T R将保持被初始化为指向这个V TA B L E, 但如果接着还有一个更晚派生的构造函数被调用,这个构造函数又将设置V P T R指向它的 V TA B L E,等.直到最后的构造函数结束。V P T R的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是从基类到更加派生 类顺序的另一个理由。
但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置V P T R指向它自己的 V TA B L E。如果函数调用使用虚机制,它将只产生通过它自己的V TA B L E的调用,而不是最后的V TA B L E(所有构造函数被调用后才会有最后的V TA B L E)。
虚拟函数调用只需要“部分的”信息,即只需要知道函数接口,而不需要对象的具体类型。但是构建一个对象,却必须知道具体的类型信息。如果你调用一个虚拟构造函数,编译器怎么知道你想构建是继承树上的哪种类型呢?所以这在逻辑上是一个悖论。

fork进程内存复制机制

现在的Unix内核(包括Linux),采用一种更为有效的方法称之为写时复制(或COW)。这种思想相当简单:父进程和子进程共享页面而不是复制页面。然而,只要页面被共享,它们就不能被修改。无论父进程和子进程何时试图写一个共享的页面,就产生一个错误,这时内核就把这个页复制到一个新的页面中并标记为可写。原来的页面仍然是写保护的:当其它进程试图写入时,内核检查写进程是否是这个页面的唯一属主;如果是,它把这个页面标记为对这个进程是可写的。

epoll中ET/LT是什么

参考:https://www.jianshu.com/p/d3442ff24ba6
1 socket IO事件
1.1 读事件
读事件:句柄从不可读变成可读,或者句柄写缓冲区有新的数据进来且超过SO_RCVLOWAT。
常见的产生读事件有如下几种:

socket有一个未清除的错误。如非阻塞的connect连接错误会使socket变成可读写状态。
非阻塞accept有新的连接进来。
socket写对端关闭,read返回0。
socket读缓冲区有新的数据进来且超过SO_RCVLOWAT
1.2 写事件
写事件:句柄从不可写变成可写,或者句柄写缓冲区有新的数据进来而且缓冲区水位高于SO_SNDLOWAT。常见的写事件事件有如下几种:

socket有一个未清除的错误。例如非阻塞connect连接出错会导致socket变成可读可写状态。
非阻塞connect连接成功后端口状态会变成可写。
socket读对端关闭,socket变成可写状态,产生SIGPIPE信号。
socket写缓冲区有新的数据进来且超过SO_SNDLOWAT
在epoll中,读事件对应EPOLLIN,写事件对应EPOLLOUT。
2 ET
句柄在发生读写事件时只会通知用户一次
ET模式主要关注fd从不可用到可用或者可用到不可用的情况。
ET只支持非阻塞模式。

2.1 应用层逻辑
ET模式下读写操作要时用wihle循环,直到读/写够足够多的数据,或者读/写到返回EAGAIN。尤其时在写大块数据时,一次write操作不足以写完全部数据,或者在读大块数据时,应用层缓冲区数据太小,一次read操作不足以读完全部数据,应用层要么一直调用while循环一直IO到EGAIN,或者自己调用epoll_ctl手动触发ET响应。

2.2 优缺点
缺点:应用层业务逻辑复杂,容易遗漏事件,很难用好。
优点:相对LT模式效率比较高。

3 LT
只要句柄一直处于可用状态,就会一直通知用户。
LT模式下,句柄读缓冲区被读空后,句柄会从可用转变未不可以用,这个时候不会通知用户。写缓冲区只要还没写满,就会一直通知用户。
LT模式支持阻塞和非阻塞两种方式。epoll默认的模式是LT。
LT下,应用层的业务逻辑比较简单,更不容易遗漏事件,更不容易出错。通常,在将数据写完后,我们会关闭句柄的写事件。

3.1 优缺点
优点:编程更符合用户直觉,业务层逻辑更简单。
缺点:效率比ET低。

3.2 ET比LT更高效得原因
ET在通知用户后,就会把fd从就绪队列里删除。而LT通知用户后fd还在就绪链表中,随着fd的增多,就绪链表越大。下次epoll要通知用户时还需要遍历整个就绪链表。遍历的性能是线性,如果fd的数量非常多,就会带来比较显著的效率下降。
同样数量的fd下,LT模式维护的就绪链表比ET的大。

3.3 非阻塞模式下的accept该用什么触发模式?
LT和ET各有优缺点。使用哪种模式取决于并发量。当并发量比较小时,比较推荐LT,因为LT模式下应用的读写逻辑比较简单,不容易遗漏事件,代码不易出错好维护,而且性能损失不大。当并发量非常大时,推荐使用ET模式,可以有效提升EPOLL效率。

3.4 LT模式下,可写状态的fd会一直触发事件,该怎么处理这个问题
方法1:每次要写数据时,将fd绑定EPOLLOUT事件,写完后将fd同EPOLLOUT从epoll中移除。
方法2:方法一中每次写数据都要操作epoll。如果数据量很少,socket很容易将数据发送出去。可以考虑改成:数据量很少时直接send,数据量很多时在采用方法1.

应用
使用ET的例子
nginx

使用LT的例子
redis

Python锁机制

参考:https://www.jianshu.com/p/0baa4cf30fd4
GIL(全局锁)
多进程是真正的并行,而多线程是伪并行,实际上它只是交替执行。由于GIL导致多线程实际上是伪并行的。因为任何Python线程执行前,必须先获得GIL锁。然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁。所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
注意:GIL并不是Python的特性,它是在实现Python解释器(CPython)时所引入的一个概念。而Python解释器,并不是只有CPython。除它之外,还有PyPy,Psyco,JPython,IronPython等。在绝大多数情况下,我们通常都认为 Python == CPython,所以也就默许了Python具有GIL锁这个事。
解决方法: 1.使用多进程代替多线程;2.更换Python解释器,不使用CPython

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值