我们是面向什么编程
在编程时,时常会说我们是面向过程编程,还是面向对象编程。其它这是把底层内存处理过程,通过编译器来抽象实现。不管是面向什么编程,其它底下都是面向内存编程。
面处对象编程时的内存模型
在编程过程中,如c++等高级语言都是面向对象编程的方式实现。都要涉及到对象的内存模型问题。如果我实例化一个对象,那这个对象在内存中是如何存放的,如果这个对象还存在继承关系,那继承的基类与派生类在内存中又是怎么样存放,存在什么样的关系呢。
本篇只说几个比较简单的问
1) 继承中基类的对象与派生类对象在内存中存放。
2)继承关系中虚函数在内存如何存放,基类与派生类虚函数是什么关系。
3)多态的实现原理
代码:
#include <iostream>
#include <string>
#include <thread>
#include <string.h>
#include <vector>
#include <iterator>
#include <algorithm>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fstream>
using namespace std;
class Base{
public:
Base(){};
Base(int age, const char* name);
void showBaseName(){cout << "This is Base name" << endl;}
virtual void virtualShowBaseName(){ cout << "virtual show Base name:" << this->mName << endl; }
virtual void virtualShowBaseAge(){ cout << "virtual show Base age:" << this->mAge << endl; }
virtual void virtualShowBaseInfo(){ cout << "virtual Base info age:" << this->mAge << endl; }
~Base(){ cout << "Base destry class" << endl; }
protected:
int showBaseAge(){ return this->mAge; }
private:
int mAge;
const char* mName;
};
class inherBase:public Base{
public:
inherBase(): Base(110, "zhangsi"){};
inherBase(const char* myName): Base(112, myName){ mName = myName; mAge = 120; }
void showinherBasebName(){cout << "inherBaseb name:" << this->mName << endl;}
void showBaseName(){cout << "this is myBase name:" << endl;}
virtual void virtualShowBaseName(){ cout << "virtual show myBase name:" << this->mName << endl; }
virtual void virtualShowMyBaseInfo(){ cout << "virtual inher Base info age:" << this->mAge << endl; }
~inherBase(){ cout << "myBase destry class" << endl; }
private:
int mAge;
const char* mName;
};
Base::Base(int age, const char* name){
this->mAge = age;
this->mName = name;
}
int main(int argc, char** argv)
{
Base* bs = new Base(11, "tudou");
inherBase* ibs = new inherBase("zhangsan");
Base* ms = static_cast<Base*>(ibs); //对象中的类型强制转换,相当于指针加上作用域,再作内存访问。
puts("------------ reload and virtual demo -----------------");
ms->showBaseName();
ibs->showBaseName();
ms->virtualShowBaseName();
ms->virtualShowBaseInfo();
ibs->virtualShowBaseName();
delete ibs;
return 0;
}
This is Base name
this is myBase name:
virtual show myBase name:zhangsan
virtual Base info age:112
virtual show myBase name:zhangsan
以简单的单继承,没有虚函数时,基类被派生类继承后,所有的数据及方法都是被派生类继承了过来,相当于在派生类的内存一块区域存放了一个以基类名为命名空间的对象;如果派生类中对基类的函数有重写过,那么并不会影响基类的函数,因为基类函数名的空间作用域是基类,而派生类重写的函数作用域是派生类中。彼此其它是在不同的作用域中,所以派生类函数的重写不会覆盖基类函数。这就可以理解为什么从派生类强制转换成基类时,此时调用的函数是基类中的函数,而不是派生类中重写的函数。
的以有如下的代码执行及结果
inherBase* ibs = new inherBase("zhangsan");
Base* ms = static_cast<Base*>(ibs); //对象中的类型强制转换,相当于指针加上作用域,再作内存访问。
ms->showBaseName();
ibs->showBaseName();
This is Base name
this is myBase name:
通过内存杳看工具也是可看到更清析的内存模型排列情况。
单继承有虚函数情况下,因为虚函数的存在,所以需要在对象内存中建一个表,称为虚函数表。这个虚函数表是通过指针来访问ptrtable,表存放的内容就是虚函数指针。这个虚函数指针是基类与派生类共享的,其它也是相当于在基类、派生类中使用指针,指向一片内存区域,这个内存区域存放的是虚函数指针(这个共享指针存放在对象内存的首地址处)。是基类,派生类使用虚指针来实现内存共享的来实现的。所以不管如何的进行指针类型的强制转换,基类,派生类都可以访问到虚函数表。
注意:因为这个虚函数表内存区域是共享的,那么如果是同名的虚函数,那么会虚函数指针只能存在一个,那么基类的虚函数就会被派生类覆盖掉,所以多态时的虚函数只能调用到基类虚函数。如果虚函数不重写,而是增加,那么基类、派生类的虚函数相互不影响。
派生类指针可以访问基类虚函数指针,也可以访问派生虚函数。但基类类型指针是无法访问到派生类虚函数。
Base* bs = new Base(11, "tudou");
inherBase* ibs = new inherBase("zhangsan");
Base* ms = static_cast<Base*>(ibs); //对象中的类型强制转换,相当于指针加上作用域,再作内存访问。
ms->virtualShowBaseName();
ibs->virtualShowBaseName(); //基类虚函数被覆盖,只执行派生类虚函数。
ms->virtualShowBaseInfo();
//ms->virtualShowMyBaseInfo(); //ERROR!!! 这个编译错误,因为基类的作用域无法访问到派生类的虚函数。
ibs->virtualShowBaseInfo();
ibs->virtualShowMyBaseInfo(); //ibs是派生类指针,可以访问到基类的虚函数,也可以访问到派生类虚函数。
关于多态的原理,是通过对象指针类型的强制转换来实现的,从上面的分析可以看出,强制转换后可派生类虚函数指针可访问基类的虚函数指针,也可以访问到派生类的虚函数指针的。通过强制转换成基类指针来访问到派生类中的成员方法(虚函数)叫多态。说白了就是基类与派生类共享内存,通过指针强制转换来实现对共享内存的访问。但因为基类与派生类又有不同的内存作用域,所以基类与派生类能访问到内存作用域不同。从而实现了状态的转换,操作了不同作用域的内存区域。