虚拟继承下的对象构造:

  由于虚拟基类对象在子类中只能保持一个实例,那么,子类构造的时候调用父类的构造函数的时候必须保证虚拟基类对象不能够重复构造。

  那么如何保证基类对象的唯一性?

  C++规定虚拟基类对象的构造只能是最外层的子类进行构造,浅层次的子类将不会在进行构造,保证了虚拟基类对象的唯一性。

  在虚拟继承体系下,子类的构造函数中必须做一个判断,设置一个标准位,用来判断虚拟基类对象是否已经构建,然后将该标志为传递给浅层次的子类,那么虚拟基类将不会再次构造。

  例如,编译器会为子类构造函数内部设置标志位

Point3D::Point3D(Point3D *this,bool _most_derived)
{
  if(_most_derived!=fales)     Point();//如果是最外层子类,构建虚拟基类对象   Point2D(false);
  Vertex(false);//将false传入说明其父类不是最外层,将不会构建虚拟基类对象
}

继承体系下的对象构造:

  必须首先将父类对象构造再构造子类对象内容。在子类构造函数中调用父类构造函数的方法可以是在成员初始化列表中显示调用构造函数,如果没有在成员初始化列表中进行构造,那么,编译器会在子类构造函数中扩充调用父类默认构造函数进行构造父类对象。


Vptr的深入探索

  在前面我们知道,Vptr必须在构造对象的时候进行初始化设置,使它指向正确的类的虚表地址。

  那么,在什么时候进行vptr的设置呢?

C++标准规定构造函数中内容执行的顺序:

1、首先调用虚拟基类(若有首先调用虚拟基类构造函数)或基类们的构造函数,构建基类子对象;

2、设置该对象的vptr,使其指向适应的虚拟函数表;

3、执行成员初始化列表中的成员初始化;

4、执行用户程序内容。

因此,当遇到在构造函数或析构函数中调用虚函数问题的时候,答案就会很明确了。

在基类构造函数中调用虚函数,将不会使用多态机制,即不会调用其子类中的虚函数,因为在基类构造的时候,vptr的设置仍指向基类的虚表,而子类还 未完成构造,vptr还未指向子类的虚表,因此,此时不会使用多态机制,仍然调用基类中的虚函数实例。这个是值得我们注意的,同时,对于这个,在《Effective C++》里也有相关的说明!

而在构造函数中使用成员函数,成员函数中调用虚函数时也不适用多态机制。只有在非构造函数中调用虚函数时才会使用多态机制。

同理,析构函数中内容的顺序正好相反:

1、调用子类析构函数中实体,完成用户程序中内容的退栈;

2、析构释放子类中不同于基类的成员;

3、调整设置vptr,使其指向基类相对应的虚表;

4、完成基类的析构。

在基类析构函数中调用虚函数,也不会实现多态机制,因为子类已经析构完毕,vptr指向基类的虚表。


赋值函数的深入探索:

未显示定义的赋值函数,编译器将视情况为类合成赋值函数,条件和合成复制构造函数的相同,只有当复制不适合 bitwise 的时候才会很成默认构造函数。

注意:复制构造函数时进行vptr的设定,而赋值函数不会进行vptr的设置,也就是说当以子类对象赋值给父类对象时,将不会改变父类对象的vptr指向,因为父类对象在构造的嘶吼已经进行了设定。

赋值函数需要进行自我识别:

加上一句,防止自我复制

if(this==&参数对象)return *this;

另外,赋值函数不能使用成员初始化列表,只有构造函数才能使用,这样就会导致,虚拟继承情况下,使用赋值函数复制对象时,会在被赋值的对象中出现多个虚拟基类对象的现象。

例如:

类A,B虚拟继承类base,C继承A和B,那么C的赋值函数就会这样写:

C& operator=(const C& c)
{
  if(this==&c)return *this;
  A::operator=(c);
  B::operator=(c);//导致出现两份虚拟基类对象实例
  //C自己的成员的复制
  return *this;
}

建议:尽量不要使用赋值函数进行虚拟继承子类对象的复制。


对象数组的构造:

对象数据的构造一般有两种方式:静态和动态

(1)静态分配

  以string类为例,string a[10];就是以静态形式构造数据,这样的数组的个数是确定的不能修改的。

  像这样的数组怎么进行构造和析构呢?

  编译器在构造数组的时候会生成一个使用默认构造函数的数组构造函数arr_new(char *p,sizeof(string),int num,构造函数地址,析构函数地址);

  同样也会生成数组析构函数,形式类似。arr_del(char *p,sizeof(string),int num,析构函数地址);

  若数组构造中间出现异常,该函数必须保证已构造的对象析构,然后释放内存。

  如果数组对象不使用默认构造函数构造对象,必须显示构造,否则,未显示构造的对象将会使用上述函数进行默认构造。

(2)动态分配

  使用new表达式进行操作,string *a=new string[10];

  new表达式分为两个步骤:首先通过内存分配所用类型大小的空间,然后再该空间上调用相应的构造函数进行构造,上述语句使用默认构造函数。

  delete 表达式则释放指针所指的内存(首先析构),大小按照指针类型的大小计算。与数组相对应的为delete[] a;

  这样就可能造成一定错误:

  当使用基类指针指向一个子类数组,则释放的时候将可能会产生错误

  Base *p=new Derived[10];

  delete[] p;

   我们知道,new出来的数组是根据Derived对象大小*10的内存空间,而delete 则是根据指针类型的大小进行析构和释放内存的,且使用类似与静态分配时的arr_del函数进行析构释放内存,这样调用的就会是基类的析构函数和基类对象 的大小。除了第一个元素,其他元素的析构都会错误的进行。

  因此建议:不要使用基类指针指向一个子类数组。

  上诉问题根据编译器而不同,微软的编译器可以支持使用基类指针释放子类数组,但是基于cfront的编译器g++将会出现错误,它会将指针类型的大小和析构函数传入它生成的arr_del函数进行析构释放,导致内存错误。

深入探索C++对象模型-5的更多相关文章

  1. 读书笔记《深度探索c++对象模型》 概述

    <深度探索c++对象模型>这本书是我工作一段时间后想更深入了解C++的底层实现知识,如内存布局.模型.内存大小.继承.虚函数表等而阅读的:此外在很多面试或者工作中,对底层的知识的足够了解也 ...

  2. 柔性数组-读《深度探索C++对象模型》有感 (转载)

    最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...

  3. 柔性数组-读《深度探索C++对象模型》有感

    最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...

  4. [读书系列] 深度探索C++对象模型 初读

    2012年底-2014年初这段时间主要用C++做手游开发,时隔3年,重新拿起<深度探索C++对象模型>这本书,感觉生疏了很多,如果按前阵子的生疏度来说,现在不借助Visual Studio ...

  5. 拾遗与填坑《深度探索C++对象模型》3.3节

    <深度探索C++对象模型>是一本好书,该书作者也是<C++ Primer>的作者,一位绝对的C++大师.诚然该书中也有多多少少的错误一直为人所诟病,但这仍然不妨碍称其为一本好书 ...

  6. 拾遗与填坑《深度探索C++对象模型》3.2节

    <深度探索C++对象模型>是一本好书,该书作者也是<C++ Primer>的作者,一位绝对的C++大师.诚然该书中也有多多少少的错误一直为人所诟病,但这仍然不妨碍称其为一本好书 ...

  7. 深度探索C++对象模型

    深度探索C++对象模型 什么是C++对象模型: 语言中直接支持面向对象程序设计的部分. 对于各个支持的底层实现机制. 抽象性与实际性之间找出平衡点, 需要知识, 经验以及许多思考. 导读 这本书是C+ ...

  8. 《深度探索C++对象模型》读书笔记(一)

    前言 今年中下旬就要找工作了,我计划从现在就开始准备一些面试中会问到的基础知识,包括C++.操作系统.计算机网络.算法和数据结构等.C++就先从这本<深度探索C++对象模型>开始.不同于& ...

  9. C++的黑科技(深入探索C++对象模型)

    周二面了腾讯,之前只投了TST内推,貌似就是TST面试了 其中有一个问题,“如何产生一个不能被继承的类”,这道题我反反复复只想到,将父类的构造函数私有,让子类不能调用,最后归结出一个单例模式,但面试官 ...

  10. 深入探索C++对象模型(一)

    再读<深入探索C++对象模型>笔记. 关于对象 C++在加入封装后(只含有数据成员和普通成员函数)的布局成本增加了多少? 答案是并没有增加布局成本.就像C struct一样,memeber ...

随机推荐

  1. WPF之核心面板(容器)控件简单介绍

    一.Canvas 1.官方表述:定义一个区域,在该区域中可以使用相对于该区域的坐标显式定位子元素. 2.对于canvas 的元素的位置,是靠控件的大小及Canvas.Top.Canvas.Left.C ...

  2. HTML5 <Canvas>文字粒子化

    文字粒子化,额或者叫小圆圈化... 1 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> ...

  3. PHP 开启 ssh2

    首先,为PHP安装SSH2扩展需要两个软件包, libssh2和ssh2(php pecl拓展). 两者的最新版本分别为libssh2-1.5.0.tar.gz和ssh2-0.12.tgz,下载地址分 ...

  4. 转:mysql性能优化的19个要点

    原文来自于:http://outofmemory.cn/mysql/mysql-performance-tips 1.为查询优化你的查询 大多数的MySQL服务器都开启了查询缓存.这是提高性最有效的方 ...

  5. 【Linux】基础配置-修改命令提示符的风格

    1,效果图: [groot]$ 2,设置步骤: 编辑~/.bashrc文件,在最后增加设置行: #显示当面目录的最后一层目录#PS1='\[\e[32m\][\u@\h \W]$\[\e[m\]'#只 ...

  6. 修改weblogic jvm启动参数

    进入: D:\Oracle\Middleware\user_projects\domains\base_domain\startWebLogic.cmd 在call 上一行增加: set USER_M ...

  7. 缩小jquery体积

    jQuery 分析 据统计,目前全世界57.3%的网站使用它.也就是说,10个网站里面,有6个使用jQuery.如果只考察使用工具库的网站,这个比例就会上升到惊人的91.7%. 虽然jQuery如此受 ...

  8. 【HDOJ】2371 Decode the Strings

    快速矩阵乘法.注意,原始字符串即为decode后的字符串.题目是要找到原始串. #include <cstdio> #include <cstring> #define MAX ...

  9. jdbc.properties 包含多种数据库驱动链接的版本。

    # Properties file with JDBC-related settings. ########## # HSQLDB # ########## #jdbc.driverClassName ...

  10. vim 的升级 安装 重装

    转载自http://blog.chinaunix.net/uid-22891521-id-2109310.html 由于一直以来在一个很old的linux版本下搞开发,里面的vim固然也是一个很old ...