死锁及其预防和处理方法
死锁的规范定义:如果一个进程在等待只能由该进程停止才能引发的事件,那么该进程就是死锁的。
(1)产生死锁的原因
·因为系统资源不足
·进程运行推进的顺序不合适
·资源分配不当等。
(2)产生死锁的四个必要条件
1.互斥条件:每个资源要么已经分配给了一个进程,要么就是可用的。
2.占有和等待条件:已经得到了某个资源的进程可以再请求新的资源。
3.不可抢占条件:已经分配给一个进程的资源不能强制性地被抢占,只能被占有它的进程显式地释放。
4.环路等待条件:死锁发生时,系统中一定有两个或者两个以上的进程组成的一条环路,该环路中的每个进程都在等待着下一个进程所占有的资源。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
(3)处理死锁的四种策略:
1.鸵鸟策略(忽略死锁)。
2.检测死锁并恢复。
3.仔细对资源进行分配,动态地避免死锁。
4.通过破坏引起死锁的四个必要条件之一,防止死锁的产生。
(4)死锁避免
死锁避免的主要算法是基于一个安全状态的概念。在任何时刻,如果没有死锁发生,并且即使所有进程忽然请求对资源的最大请求,也仍然存在某种调度次序能够使得每一个进程运行完毕,则称该状态是安全的。从安全状态出发,系统能够保证所有进程都能完成,而从不安全状态出发,就没有这样的保证。
·银行家算法:判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求,如果满足请求后系统仍然是安全的,就予以分配。不安全状态不一定引起死锁,因为客户不一定需要其最大贷款额度。
C++
1.虚函数的实现原理
虚函数表的创建和继承
a.基类的虚函数表的创建:首先在基类声明中找到所有的虚函数,按照其声明顺序,编码0,1,2,3,4......,然后按照此声明顺序为基类创建一个虚函数表,其内容就是指向这些虚函数的函数指针,按照虚函数声明的顺序将这些虚函数的地址填入虚函数表中,其内容就是指向这些虚函数的函数指针,按照虚函数声明的顺序将这些虚函数的地址填入虚函数表中。例如若show放在虚函数声明的第二位,则在虚函数表中也放在第二位。
b.对于子类的虚函数表:首先将基类的虚函数表复制到该子类的虚函数表中。若子类重写了基类的虚函数show,则将子类的虚函数表中存放show的函数地址(为重写前存放的是子类的show虚函数的函数地址)更新为重写后函数的函数指针。若子类增加了一些虚函数的声明,则将这些函数的地址加到该类虚函数表的后面。
·通过虚函数表访问对象的方法
当执行Base->show()时,要观察show在Base基类中声明的是虚函数还是非虚函数。若为虚函数将使用动态联编(使用虚函数表决定如何调用函数),若为非函数则使用动态联编(根据调用指针Base的类型来确定调用哪个类的成员函数)。此处假设show为虚函数,首先:由于检测到Base指针类型所指的类Base中show定义为虚函数,因此找到Base所指的对象,访问对象得到该对象所属类的虚函数表地址。其次:查找show在Base类中声明的位置在Base类中所有虚函数声明中的位序。然后到Base所指对象的所属类的虚函数表中访问该位序的函数指针,从而得到要执行的函数。
为什么要把析构函数定义为虚函数?
new出来的是子类son的对象,采用一个父类father的指针来接收,故在析构的时候,编译器因为只知道这个指针是父类的,所以只将父类部分的内存析构了,而不会去析构子类的内存,就造成了内存泄漏。基类析构函数定义为虚拟函数的时候,在子类的对象的首地址开始会有一块基类的虚函数表拷贝,在析构子类对象的时候会删除此虚函数表,此时会调用基类的析构函数,所以此时内存是安全的。
为什么虚函数比普通函数慢?
因为虚函数要通过查找虚函数表的方法访问。
为什么构造函数不能是虚函数?
构造函数不可以是虚函数的,这个很显然,毕竟虚函数都对应一个虚函数表,虚函数表是存在对象内存空间的,如果构造函数是虚的,就需要一个虚函数表来调用,但是类还没实例化没有内存空间就没有虚函数表,这就是个死循环。
内联函数、构造函数、静态成员函数可以是虚函数吗?
首先虚函数是针对对象而言,在运行时候才进行动态联编的。
内联函数是在编译阶段进行展开,inline关键字作为提示符告诉编译器此函数作为内联函数希望在编译阶段展开,但是,编译器并不一定要展开。所以可以声明为虚函数。
构造函数无法是虚函数,因为调用虚函数需要虚函数表指针,而在执行构造函数之前是没有虚函数表指针的。
静态成员函数不可以是虚函数。静态函数是属于类的,不属于对象本身,自然无法有自己的虚函数表指针。
区分重载、重写和隐藏
注意三个概念的适用范围:处于同一个类中的函数才会出现重载。处于父类和子类中的函数才会出现重写和隐藏。
重载:同一类中,函数名相同,但参数列表不同。
重写:父子类中,函数名相同,参数列表相同,且有virtual修饰。
隐藏:父子类中,函数名相同,参数列表相同,但没有virtual修饰;函数名相同,参数列表不同,无论有无virtual修饰都是隐藏。
基类中:
(1)virtual void show(); //是虚函数
(2)void show(int); //不是虚函数
子类中:
(3)void show(); //是虚函数
(4)void show(int); //不是虚函数
1,2构成重载,3,4构成重载,1,3构成重写,2,4构成隐藏。另外2,3也会构成隐藏,子类对象无法访问基类的void show(int)成员方法,但是由于子类中4的存在导致了子类对象也可以直接调用void show(int)函数,不过此时调用的函数不再是基类中定义的void show(int)函数2,而是子类中的与3重载的4号函数。
C对象和C++对象的区别(struct和class)
C++中的struct对C中的struct进行扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。struct能包含成员函数,struct能继承,struct能实现多态。
a·默认的继承访问权限。struct是public的,class是private的。
b·struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的。