2016.9.28 海航科技
1. 进程和线程的区别
1)l 程序是代码的集合;进程:代码的一次执行(包含多个线程)。
l 线程:进程中一个独立的单一的执行流程。
2) 进程是一个独立功能的程序,关于某个数据集合的一次运行活动状态。它可以申请和拥有系统资源,是一个活动的实体。包括程序的代码还包括当前的活动,如:程序计数器的值和处理寄存器的内容来表示。
一个进程中包含若干个线程。
3)线程和进程的区别:
l 子线程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器。多线程主要似乎为了节约CPU时间,线程运行需要使用计算机的内存资源和CPU。
l 地址空间和其它资源:进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程中不可见。
l 通信:多线程可共享相同的地址空间,共同分享同一个进程。
l 调度和切换:线程上下文切换比进程上下文切换要快的多。
l总结
3.区别
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
2) 线程的划分尺度小于进程,使得多线程程序的并发性高。
3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
优缺点:.优缺点
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP(Symmetrical Multi-Processing,双CPU)机器上运行,而进程则可以跨机器迁移。
2.多态
同样的调用语句有多种不同的表现形式
发生的3条件:
1) 继承
2) 虚函数重写
3)父类指针(或引用)只想子类对象
//多态:同样的语句有多种不同的表现形式
//根据实际对象类型决定函数调用语句
#include <iostream>
using namespace std;
//HeroFighter AdvFighter EnergyFighter
class HeroFighter
{
public:
virtual int power() //vitrual关键字实现多态
{
return 10;
}
};
class EnergyFighter
{
public:
int attack()
{
return 15;
}
};
//::public HeroFighter
class AdvFighter:public HeroFighter
{
public:
int power()
{
return 20;
}
};
void playObj(HeroFighter *hf,EnergyFighter *ef)
{
if (hf->power()>ef->attack())//hf->power将发生多态
{
cout<<"主角win\n";
}
else
{
cout<<"主角挂了\n";
}
}
int main()
{
HeroFighter hf;
AdvFighter Advhf;
EnergyFighter ef;
playObj(&hf,&ef);
playObj(&Advhf,&ef);
system("pause");
return 0;
}
int main01()
{
/*
HeroFighter hf;
AdvFighter Advhf;
EnergyFighter ef;
if (hf.power()>ef.attack())
{
cout<<"主角win\n";
}
else
{
cout<<"主角挂了\n";
}
if (Advhf.power()>ef.attack())
{
cout<<"Adv 主角win\n";
}
else
{
cout<<"Adv 主角挂了\n";
}
*/
system("pause");
return 0;
}
引申实质:
C++中多态的实现原理 当类中声明虚函数时,编译器会在类中生成一个虚函数表 虚函数表是一个存储类成员函数指针的数据结构 虚函数表是由编译器自动生成与维护的 virtual成员函数会被编译器放入虚函数表中 存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针) |
|
|
说明1: 通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多。 说明2: 出于效率考虑,没有必要将所有成员函数都声明为虚函数 |
5、多态原理研究(证明VPTR指针的存在)
C++编译器内部实现的时候,通过virtual关键字,内部帮我们在父类子类添加了虚函数指针和虚函数表,以下是证明虚函数指针的存在的方法 |
#include "iostream" using namespace std;
class AA { public: virtual void print() { printf("dddd\n"); } protected: private: int b; }; void main() { printf("AA%d \n", sizeof(AA)); //8,VPTR4字节,b字节 system("pause"); } |
6、虚函数表指针(VPTR)被编译器初始化的过程
|
对象在创建的时,由编译器对VPTR指针进行初始化 只有当对象的构造完全结束后VPTR的指向才最终确定 父类对象的VPTR指向父类虚函数表 子类对象的VPTR指向子类虚函数表 |
7、为什么要定义虚析构函数
//在父类中声明虚析构函数的原因 //通过父类指针,把所有的子类析构函数都执行一遍。。。 void howtoDel(Parent *pbase) { delete pbase; } void mainobj() { Parent *p1 = new Parent(); p1->print(); delete p1; } void main() { Child *pc1 = new Child(); howtoDel(pc1); //mainobj(); system("pause"); } |
8、基类和子类对象指针++混搭风
class Parent01 { protected: int i; int j; public: virtual void f() { cout<<"Parent01::f"<<endl; } }; //一次偶然的成功,比必然的失败更可怕 class Child01 : public Parent01 { public: int k; public: Child01(int i, int j) { printf("Child01:...do\n"); } virtual void f() { printf("Child01::f()...do\n"); } }; void howToF(Parent01 *pBase) { pBase->f(); } int main() { int i = 0; Parent01* p = NULL; Child01* c = NULL; //可以使用赋值兼容性原则,是用在多态的地方 //不要轻易通过父类指针p++,来执行函数操作 //问题的本质 子类指针 和父类指针 步长可能不一样。。。 Child01 ca[3] = {Child01(1, 2), Child01(3, 4), Child01(5, 6)}; p = ca; //第一个子类对象赋值给p,p是基类指针, c = ca; p->f(); //有多态发生 //c->f(); // p++; //c++; p->f();//有多态发生 //c->f() // for (i=0; i<3; i++) // { // howToF(&(ca[i])); // } system("pause"); return 0; } |
3. 面向对象