引入

  定义一个类的对象,首先系统已经给这个对象分配了空间,然后会调用构造函数(说明:假设存在构造函数)。一个类有多个对象,当程序中调用对象的某个函数时,有可能要访问到这个对象的成员变量。而对于同一个类的每一个对象,都是共享同一份类函数。对象有单独的变量,但是没有单独的函数,所以当调用函数时,系统必须让函数知道这是哪个对象的操作,从而确定成员变量是哪个对象的。这种用于对成员变量归属对像进行区分的东西,就叫做this指针。事实上它就是对象的地址.

记得孙鑫VC++视频教程里有一段剖析MFC的代码,大意就是 CTESTAPP类是CWINAPP的子类,而CTESTAPP创建一个全局对象时,在CWINAPP的构造函数里面用了this指针,但是这里this指针指向的的是CTESTAPP的对象,而不是所在类的对象,也许读者这时候对这个this指针有点糊涂了! 不要怕!马上让你明明白白! 请看下面一段代码:

#include <iostream.h>
class PARENT{//基类     int d; public: PARENT(){ d=1; cout<<"PARENT this ="<<this<<endl;              cout<<"d="<<this->d<<endl;     } };
class CHILD : public PARENT//子类  {  int b;    public: CHILD():PARENT(){  b=2;  cout<<"CHILD this = "<<this<<endl;  cout<<"b="<<this->b<<endl;     } };
int main(int argc, char* argv[])
{   CHILD cb;//CHILD对象  cout<<"CHILD object cb's addr is="<<&cb<<endl;  return 0;
} 

这段代码和上面提到的MFC的代码原理一样!此代码运行的结果你会发现this的值都一样!而且this都是指向cb对象的!
因为this指针式在创建一个对象时,隐含的将对象的地址赋予一个指针,那就是this指针。在创建对象cb时,先讲cb的首地址赋给this,根据继承性,首先调用基类的构造函数,虽然此时cb对象还未完全创建(必须调用完子类的构造函数时,此时对象才会创建完毕),但是此时cb对象的基类部分已经构造完毕,所以这时候的this指针可以看成是cb的this指针,但是只能调用基类PARENT的数据成员。如果此时你在PARENT()里加上一句cout<<"b="<<this->b<<endl;让其调用子类的数据成员,则会报错!因为this指向的对象没有构造子类的部分!在执行完基类的构造函数进入子类的构造函数后,这时候this指向的对象构造完成,这时候this指针也就是一个真正的的指向cb的常指针了。

这时候你也不难理解了MFC当中CWINAPP传递的this指针是指向子类CTESTAPP的对象,而不是所在类CWINAPP了!这里在基类中使用this的意思有点像,由于子类对象没构造好,但是指向对象的指针已近指向那个对象了,早晚对象都会构造好的,那不如先拿this指针代替子类对象用!

补充:构造函数为什么不能是虚函数

1. 从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,这大家都知道,可是这个指向vtable的指针其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。
2. 从使用角度,虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。
3. 构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它但析构却不一定,我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。
4. 从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有必要成为虚函数。
5. 当一个构造函数被调用时,它做的首要的事情之一是初始化它的VPTR。因此,它只能知道它是“当前”类的,而完全忽视这个对象后面是否还有继承者。当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码——既不是为基类,也不是为它的派生类(因为类不知道谁继承它)。所以它使用的VPTR必须是对于这个类的VTABLE。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内,VPTR将保持被初始化为指向这个VTABLE, 但如果接着还有一个更晚派生的构造函数被调用,这个构造函数又将设置VPTR指向它的 VTABLE,等.直到最后的构造函数结束。VPTR的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是从基类到更加派生类顺序的另一个理由。但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置VPTR指向它自己的VTABLE。如果函数调用使用虚机制,它将只产生通过它自己的VTABLE的调用,而不是最后的VTABLE(所有构造函数被调用后才会有最后的VTABLE)。
Note:
1. 如果我们定义了一个构造函数,编译器就不会再为我们生成默认构造函数了。
2. 编译器生成的析构函数是非虚的,除非是一个子类,其父类有个虚析构,此时的函数虚特性来自父类。
3. 有虚函数的类,几乎可以确定要有个虚析构函数。
4. 如果一个类不可能是基类就不要申明析构函数为虚函数,虚函数是要耗费空间的。
5. 析构函数的异常退出会导致析构不完全,从而有内存泄露。最好是提供一个管理类,在管理类中提供一个方法来析构,调用者再根据这个方法的结果决定下一步的操作。
6. 在构造函数不要调用虚函数。在基类构造的时候,虚函数是非虚,不会走到派生类中,既是采用的静态绑定。显然的是:当我们构造一个子类的对象时,先调用基类的构造函数,构造子类中基类部分,子类还没有构造,还没有初始化,如果在基类的构造中调用虚函数,如果可以的话就是调用一个还没有被初始化的对象,那是很危险的,所以C++中是不可以在构造父类对象部分的时候调用子类的虚函数实现。但是不是说你不可以那么写程序,你这么写,编译器也不会报错。只是你如果这么写的话编译器不会给你调用子类的实现,而是还是调用基类的实现。
7.
在析构函数中也不要调用虚函数。在析构的时候会首先调用子类的析构函数,析构掉对象中的子类部分,然后在调用基类的析构函数析构基类部分,如果在基类的析构函数里面调用虚函数,会导致其调用已经析构了的子类对象里面的函数,这是非常危险的。
8. 记得在写派生类的拷贝函数时,调用基类的拷贝函数拷贝基类的部分,不能忘记了。
参考链接:http://blog.sina.com.cn/s/blog_721f7e290100uzo1.html
http://www.cnblogs.com/lixiaohui-ambition/archive/2012/08/28/2660708.html

http://www.cnblogs.com/cswuyg/archive/2010/08/21/1805153.html





谈谈基类与子类的this指针(C++)相关推荐

  1. 【C++】多态问题:基类调用子类的protected或者private函数

    1.问题描述 如果在基类中虚函数是public,子类中重载时标记为protected或者private函数,是否还能访问这个函数? 答案是: 基类指针指向子类时,可以访问,并且访问的是子类重载后的函数 ...

  2. 关于基类和子类构造函数的问题

    关于基类和子类构造函数的问题 情况一:在一个类无自定义构造函数时,用这个类定义一个对象a,再定义另个一个对象b时,使用b(a)这种默认的拷贝构造函数时会出错,编译器提示a没有被定义. 如下面程序: # ...

  3. moviepy音视频剪辑:视频基类VideoClip子类VideoFileClip、CompositeVideoClip、ImageSequenceClip介绍

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 在<moviepy音视频剪辑:moviepy中的剪辑相关类及关系>介绍了VideoClip主要有六个直接子类(VideoFileClip ...

  4. 使用“override”声明的成员函数不能重写基类成员_C++日志(三十五)虚基类与其子类的构造函数...

    第三十四篇日志中提到,子类对象调用父类同名的函数时需要使用作用域标识符进行限定,这是在任何继承情况下都可以使用的普适方法. 问题引入: 考虑一种特殊的情况:子类全部或部分的基类(称为中间基类)同是继承 ...

  5. Cpp 对象模型探索 / 虚继承带虚函数的基类的子类的内存布局

    源码 class Base { public:Base() {}virtual void func() {}int bi_; };class Son:virtual public Base { pub ...

  6. c++ 虚函数多态、纯虚函数、虚函数表指针、虚基类表指针详解

    文章目录 静态多态.动态多态 虚函数 哪些函数类型不可以被定义成虚函数? 虚函数的访问方式 析构函数中的虚函数 虚函数表指针 vptr 多继承下的虚函数表 虚基类表指针 bptr 纯虚函数 抽象类 虚 ...

  7. Swift基础语法: 30 - Swift的基类, 子类, 重写, 重写方法, 重写属性, 防止重写

    前面我们讲了有关于脚本语法的基本认识以及使用, 现在让我们来看看Swift的其他东西: 1.基类 所谓的基类, 就是OC中的父类, 不继承任何类, 就叫做基类, 让我们来看看例子: class Veh ...

  8. java子类怎么编译_java – 无法编译从基类实现抽象方法的子类

    编译我已经定义的基类的子类有一个问题,它有一个单独的方法,而每个子类都实现了抽象基类方法,但是 javac说他们甚至没有在子类中明确定义它们. DbModel.java(基类) package com ...

  9. 派生类到基类的转换 和基类到派生类的转换

    一. 基类与派生类的转换     3种继承方式(公用.保护.私有继承)中,公用派生类才是基类真正的子类型,它完整地继承了基类的功能.     不同类型数据之间在一定条件下可以进行类型的转换.基类与派生 ...

最新文章

  1. springmvc常用注解标签详解
  2. 1.spring boot要求最低jdk1.8,平安默认1.6问题,-》安装JDK1.8 2.maven 3.3.3要求最低jdk1.7-安装jdk 1.8...
  3. 北京python培训班价格-北京Python编程培训多少钱
  4. python hashlib模块_python3 hashlib模块
  5. Apache Beam发布第一个稳定版本
  6. python与vfp做桌面数据库_VFP数据库系统开发实例(附图)
  7. OpenCV——释放时错误[SourceReaderCB::~SourceReaderCB terminating async callback]解决方案
  8. 全面剖析支付宝服务窗功能二次开发
  9. java对象转xml jackson_五分钟Jackson入门(三) JSON数据类XML转换(附项目源码)
  10. 【LeetCode之C#解法】 移动零、爬楼梯
  11. linux系统键盘记录器,可截获到 QQ 密码 键盘记录器源码
  12. wordpress蓝色简洁中文杂志主题wordpress模板
  13. 从学校到现在的一个总结
  14. 深搜--1-n的全排列
  15. java dll 调用方法_关于Java调用dll的方法 | 学步园
  16. AB_PLC编程软件RSLogix_500_与PLC通讯详细说明
  17. element tree不刷新视图_安卓从入门到进阶第五章(视图查看)
  18. Mac双开微信(2种方法)、Win多开微信
  19. android 实现 bilili 动画播放效果
  20. Informix 12.10版本新特性-2

热门文章

  1. 等不到那人,回不到人间——dbGet(四)
  2. 自己总结一下wpf image source 绑定的几种方式
  3. 从零开始学ios开发(十四):Navigation Controllers and Table Views(上)
  4. HT for Web中3D流动效果的实现与应用
  5. ubuntu 设置定时任务
  6. cssd oracle,Oracle RAC /etc/init.d/init.cssd startcheck
  7. linux中线程的问题,linux中的线程问题
  8. python语法31[变量的作用域+global]
  9. 小黄鸭c语言程序代码,新年的小黄鸭 - 题目 - Universal Online Judge
  10. Java多线程(三)——多线程实现同步