类对象继承之类的云云

先说好,这东西可以算是原创,不过在网上、书上看了不少东西而已。不一一致谢,总之是感谢。
 
类与对象
面向对象的主要实现手段就是类,但是C++的类、对象在实现上决不能用所谓的面向对象去理解。或者说不只是实现上,整个在编程上,所谓 面向对象远远不能像其吹嘘的那样用现实世界中的理念来理解。(c++之父说过类似的意思)
数据和在其上的一系列操作封装到一起,就是所谓的类,而一个类的具体化,就是一个对象,也可以叫做类的实例化。
而继承无非就是为了代码复用和理顺加强层次关系。
 
内存布局
对于C++中的类和对象,在内存布局上,编译器是怎么处理的呢?
其实每个实例对象只是对类中的数据成员初始化,内存映射中每个对象仅仅保留属于自己的那部分数据成员的副本,而成员函数对于整个类而言只有一份,每个实例共享这一份成员函数,基于此来说,对于对象,其数据和操作在内存布局上分离的。当然虚函数又是通过其他手段实现,每个类保有一份虚函数表(vtbl),vtbl第一个slot里保存的是class_info内容,之后每一个slot是此类的一个虚函数;而每一个对象包含一个虚函数指针vptr,指向自己的虚函数表。
当在程序中通过对象调用其方法(通过.或者->访问成员函数)时,编译器会将这个成员函数转换为常见的全局函数的形式,只是多出来一个参数(第一个),将this指针传入这个参数。这样原本应该每个对象都有的数据成员、成员函数被分离后,又重组到了一起。如果通过父类指针指向子类对象,然后通过父类指针访问虚函数时,程序会在运行时根据vptr调用正确的虚函数以表现多态,这也叫做动态绑定。
 
访问控制
C++中有三种访问控制符:public、protected、private。
之前看过一篇东西,大致是说访问控制符到底是要控制谁访问?他给出的答案是函数,因为就算用类封装了,但是编译时成员函数还是会被解析成全局函数,而在主程序中都是下辖在main函数中,所以限制主体一直是函数。
public:允许所有访问。
protected:只允许在类及派生类内访问,或者是友元(函数或类)。
private:只允许在类内访问,或者是友元。
用之前的解释,限制的主体是函数,确实更加清晰,而且没有错误。比如在main函数里通过类对象调用protected或者private函数时就会发生错误,因为main函数不在类或其派生类内,那么不符合规则。
 
一般情况下或者说没有明确指明时,成员函数通过this指针访问实例的数据成员(可能是私有或者保护的)。但是如果给成员函数指明一个实例呢(非默认的this)?这时就出现了一个实例可以访问另一个实例的数据成员的情况。说到底,访问实例数据成员的是成员函数,跟哪一个实例去调用没有关系,而成员函数访问实例的数据成员本该如此。
比如有两个对象,obj1,obj2,都属于同一个类A,这个类有一个public成员函数func()。obj1.func()编译时,传入this指针,func()中操作obj1的自身数据成员(私有或者保护)。但如果函数是func(A &obj)的样子,然后obj1.func(obj2)。在编译时,将obj2传给成员函数func(),成员函数访问实例obj2的数据成员,但外形上看起来却是boj1访问了obj2的数据成员。
又比如,拷贝构造函数,在拷贝构造函数中是可以操作被拷贝者的私有成员的。
 
继承
接下来在说下这继承,目前我的看法还是两点,第一继承可以更好的复用代码;第二继承可以组织起一棵相互关联、层次清晰的继承树。第一点很好理解,在内存上,子类对象占据的空间一定不会小于父类对象,因为父类对象需要开辟存储非静态数据成员的空间子类对象一定会开辟。这就是所谓的继承,不需要你再为子类编写重复的代码。第二点应该不需要多说了,一层一层继承,层次结构当然更有组织性。
 
C++中有三种继承,跟访问控制有关,private,protected,public:
面向对象中,有两个概念,一个是接口、一个是实现。那么在这里public继承就是接口继承,而protected、private继承就是实现继承。
public,最常用的一种继承方式,符合LSP原则,也就是说子类可以代替父类完成父类接口所声明的行为。而父类成员的访问控制状况不发生改变。
protected,不符合LSP原则,子类不能替换父类,即使强制转换,得到的也是空指针。将父类的public访问级别降低到protected,protected不变。子类不再具有父类的接口,但是这些东西(protected、public)还是可以传承下去。
private,不符合LSP原则,子类不能替换父类,即使强制转换,得到的也是空指针。将父类的public、protected访问级别降低到private。相当于祖宗的那些东西延续到这个类时就结束了。
单纯的理解,我认为public继承才算是继承,符合LSP,是IS A。后两种已经不能算是继承了,只是一种手段,在自身类中使用另一个类的公有、保护成员的手段。而protected和private区别只是在传递上的不同,前者可以继续传递下去,后者到此为止。
虚继承
这块东西可以说是要知道但不一定能用到,就目前来说,我了解的也比较少。虚继承就是为了共享,lippman说的。我看了一些例子,都是拿多重继承来说事情的,我觉得除了多重继承大概用不到了吧。
在多重继承里,子类可以继承两个父类,但是如果两个父类又有同一个父类的时候,子类就会继承两个最上层父类,姑且叫做爷爷类。闻起来就是坏味道了。那么怎么解决呢,好办,让子类继承一份爷爷类。
语法上,在父类继承爷爷类时,在public后加上virtual就好了。这样爷爷类就成了父类的虚基类,在内存布局上也只有一份爷爷类对象,就是唯一的subobject。这样就解决了爷爷类的唯一性,接下来还得解决访问的问题,因为是共享的,就不能像普通基类那样,继承体系中的每个子类都保有一份,虚基类subobject是唯一的,既然是virtual的,解决办法也是跟virtual相关,把它放在vtbl中,继承体系中每一个子类对象都增加一个bptr指向这个subobject, 这也就是代价了。
其中需要注意的是,因为subobject是唯一的,所以它之下集成体系中的每个类构造函数中都显示必须调用它的构造函数(如果是非默认的),因为这个虚基类之下的基层体系中哪一个类先构造是未可知的,然而虚基类又是唯一的,所以初始化是个问题,这样每一个子孙都有对它进行初始化,无论哪个先出现,它都安全的初始化了,初始化之后其他类中的相关初始化语句就被屏蔽了。
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值