1.先内含一个类

类声明

class Point
{public :Point(int x = 0, int y = 0):X(x), Y(y){};Point(const Point& Temp);Point& operator=(const Point& Temp);virtual ~Point() {};virtual int z() { return 0; }inline int getX(void) const { return X; }inline int getY(void) const { return Y; }inline void setX(int x) { X = x; }inline void setY(int y) { Y = y; }
protected:int X, Y;
};class Line
{public:Line(int x0, int y0, int x1, int y1);Line(const Point& begin, const Point& end) : Begin(begin), End(end) {}protected:Point Begin, End;
};

某些实现


Line::Line(int x0, int y0, int x1, int y1)
{this->Begin.setX(x0);this->Begin.setY(y0);this->Begin.setX(x1);this->Begin.setY(y1);
}
Point&  Point::operator=(const Point& Temp)
{this->X = Temp.X;this->Y = Temp.Y;return *this;
}Point::Point(const Point& Temp)
{this->X = Temp.X;this->Y = Temp.Y;
}

调用

int main()
{Line a(1,2,3,4);Line b = a;return 0;
}

结果

①我们可以看到Line这个类中内含了另外一个类的对象,而且Line并未显示拥有一个=赋值运算符的重载,也没有拥有一个拷贝构造函数 ,但是却可以把一个Line b = a(拷贝构造函数),c = a;(赋值运算符的重载)
所以编译器为我们隐式生成了这些函数
②当内含一个对象的时候,会先调用内含对象的构造函数,在调用自身的构造函数
③由于Point声明了一个拷贝构造函数,一个重载赋值运算符,一个虚析构函数,所以Line的隐式拷贝构造函数,隐式重载赋值运算符,和析构函数(继承是虚的,本例子中是内含 是nontrivial)
可能生成的Line的析构函数

Line::~Line(Line *this)
{this->End.Point::~Point();this->Begin.Point::~Point();
}

2.构造函数调用顺序

1、先调用基类的构造函数,调用顺序与继承顺序保持一致

2、再调用派生类中的内含的对象的构造函数,调用顺序与内含对象的声明顺序保持一致,与初始化列表中的顺序无关。

3、最后调用派生类的构造函数。

而析构函数与构造函数的顺序完全相反。

3.虚拟继承(Virtual Inheritance)

class Point3d : public virtual Point
{public:Point3d(float x = 0.0,float y = 0.0,float z = 0.0):Point(x,y),_z(z){}Poin3d(const Point3d& rhs): Point(rhs),_z(rhs._z){}~Point3d();Point3d& operator = (const Point3d&);virtual float z(){return _z;}
protected:float _z;
};

传统的“constructor扩展现象”并没有用,这是因为virtual base class的“共享性”原因:

//C++伪码
//不合法constructor扩展
Point3d*
Point3d::Point3d(Point3d* this,float x = 0.0,float y = 0.0,float z = 0.0)
{this->Point::Point(x,y);this->vptr_Point3d = _vtbl_Point3d;this->_vptr_Point3d_Point = _vtbl_Point3d_Point;this->_z = rhs._z;return this;
}

想象如下的三种类的派生情况:

class Vertex : virtual public Point{..};
class Vertex3d : public Point3d,public Vertex{...}
class PVertex : public Vertex3d{...}


Vertex的constructor必须也调用Point的constructor,然而,当Point3d和Vertex同为Vertex3d的subobject时,它们对Point constructor掉头操作一定不可以发生,应该是作为最底层的class,有责任去将Point初始化。

传统的策略情况下扩充的constructor,会导致constructor中更多的扩充内容,用以指示virtual base class constructor应不应该被调用,constructor的函数体必须因条件测试参数,决定调不调用相关的virtual base class constructors。如下是正确的Pointd3d的constructor:

//C++伪码
//在virtual base class情况下的constructor扩充
Point3d*
Point3d::Point3d(Point3d* this,float x = 0.0,float y = 0.0,float z = 0.0)
{if(_most_derived != false)this->Point::Point(x,y);this->vptr_Point3d = _vtbl_Point3d;this->_vptr_Point3d_Point = _vtbl_Point3d_Point;this->_z = rhs._z;return this;
}

在更深的继承体系下,Vertex3d,调用Point3d和Vertex的constructor时,总会把_most_derived设置为false,于是就压制了两个constructors中对Point constructor的调用:

//C++伪码
//在virtual base class情况下的constructor扩充
Vertex3d*
Vertex3d::Vertex3d(Vertex3d* this,float x = 0.0,float y = 0.0,float z = 0.0)
{if(_most_derived != false)this->Point::Point(x,y);//调用上层base classes//设定_most_derived为flasethis->Point3d::Point3d(false,x,y,z);this->Vertex::Vertex(false,x,y);//安插vptrs//安插user codereturn this;
}

这样策略下以保证语意的正确无误,当我们定义Point3d和Vertex3d cv时,都可以正确的调用Point constructor。

某些编译器支持把一个constructor一分为二,一个针对完整的object定义,另一个针对如果完整object是subobject;完整的版本,无条件调用virtual base constructor,设置所有的vptr;subobject版本则不调用virtual base constructor,也可能不设定vptr。

4.vptr初始化语意学(The Semantics of the vptr Initialization)

当我们定义一个PVertex object时,constructors调用顺序:

Point(x,y);
Point3d(x,y,z);
Vertex(x,y,z);
Vertex3d(x,y,z);
PVertex(x,y,z);

假设这个继承体系中每一个class都定义了virtual function size(),此函数返回class的大小。当我们如下操作,传回PVertex大小:

PVertex pv;
Poin3d p3d;Point* pt = & pv;
pt->size();

假设这个集成体系中每一个constructor内含一个调用操作:
就是在每一个基类的构造函数内部写下一个虚函数size()

Point3d::Point3d(float x,float y,float z):_x(x),_y(y),_z(z)
{if(spyOn)cerr << "Within Point3d::Point3d()"<< "size" << size() << endl;
}

那么 他会产生多态调用吗?
对于如上调用而言,如果调用操作限制在constructor(或destructor)中,每一个调用都是静态的,不会是动态的

在执行一个constructor时,必须限制一组virtual function候选名单,决定virtual function的候选名单的virtual table。virtual table是通过vptr来处理的。所以编译器为了控制一个class中所有作用的函数,编译器系统只要简单的控制vptr的初始化和操作即可。设定vptr是编译器的责任,程序员不需要操心。

vptr在constructor中的初始化时间是:在base class constructors调用操作之后,但是在程序员提供的代码或是member initialization list中所有的member初始化操作之前。

每一个constructor都一直等到起base class constructors执行完毕之后才设定其对象的vptr,那么每次它都能够正确的调用virtual function实例

令每一个base class constructor设定其对象的vptr,使它指向相关的virtual table之后,构造中的对象就可以严格而正确的“构造过程中所幻化出来的每一个class”的对象。constructor执行通常如下:

①在derived class constructor中,“所有virtual base classes”及“上一层base class”的constructor会被调用。
②上述完成之后,对象的vptr被初始化,指向vtbl。
③如果有member initialization list的话,将在constructor体内扩展开来,这必须在vptr被设定之后才做,以免有一个virtual member function被调用。
④执行程序员所提供的代码。
构造函数的调用顺序 从根到叶子 从内到外 也就是说只有当自己的构造函数被调用完成之后,才是真正的构造完毕

vptr必须设定的情况
1.当一个完整的对象被构造起来的时候。
②当一个subobject constructor 调用了一个virtual function时

继承体系下的对象构造相关推荐

  1. C++继承体系下的对象构造

    继承体系下的对象构造 继承下的对象构造 虚拟继承 初始化"虚基类子对象" vptr的设置 总结 继承下的对象构造 class Point{public:Point(float x ...

  2. c++无继承情况下的对象构造

    无继承情况下的对象构造 C struct的Point声明 在C和C++中有什么区别? 抽象数据类型 包含虚函数的Point声明 自定义构造函数中会安插初始化vptr的代码 以成员为基础的赋值操作 C ...

  3. “无继承”情况下的对象构造

    先留个底,晚上开始看这章 转载于:https://www.cnblogs.com/kddsly/archive/2010/03/24/1693384.html

  4. 理解SQL Server中的权限体系(下)----安全对象和权限

    在开始阅读本文之前,请确保你已经阅读过上一篇文章,文章地址: 理解SQL Server中的权限体系(上)----主体 简介 在上一篇文章中,我对主体的概念做了全面的阐述.本篇文章接着讲述主体所作用的安 ...

  5. Request和Response-学习笔记01【Request_原理和继承体系、Request_获取请求数据】

    Java后端 学习路线 笔记汇总表[黑马程序员] Request和Response-学习笔记01[Request_原理和继承体系.Request_获取请求数据][day01] Request和Resp ...

  6. 复合继承关系下的构造和析构

    继承关系下的构造和析构 看一下测试代码: /** @filename: Inheritance.cpp* @author: Tanswer* @date: 2018年01月31日 14:59:28* ...

  7. C++编程进阶5(内联函数、如何降低编译成本、处理继承体系中同名不同参的成员函数、私有虚函数)

    十七.内联函数 在https://blog.csdn.net/Master_Cui/article/details/106391552中,已经简单的说过内联函数的作用. 函数体较小的内联函数经过编译后 ...

  8. 继承情况下构造方法的调用过程-java

    继承情况下构造方法的调用过程-java super关键字:可以看做是直接父类对象的引用.每个子类对象都会有一个super引用 指向其直接父类对象.super可以:1.调用成员变量 super.colo ...

  9. 云原生体系下的技海浮沉与理论探索

    作者 | 王银利(芸峥) 1 . 概述 攻技者,短之:理论者,长之:践行者,胜之.可以这么说,一座城市的良心就体现在下水道上,不论这座城市有多少高楼大厦,建设得有多么宏伟,只要是下雨天,雨水就变成了城 ...

  10. Python 异常继承体系

    Python 异常继承体系(version python3.4) 以下是按照 The Python Standard Library  翻译 The class hierarchy for built ...

最新文章

  1. SAP MM Movement Type 503的使用
  2. 美国国家科学院发布《材料研究前沿:十年调查》
  3. 【CSS】选择器优先级
  4. 怎么样设置关闭网页再次登录网页是正常登录状态_学籍系统出现“该账号已登录,不能重复登录”怎么办?...
  5. VTK修炼之道45:图形进阶_vtkPolyData属性数据
  6. [2016-03-15]rabbitmq notes
  7. 查看目录是否为内存盘_Linux buff/cache内存释放
  8. Mysql入门的10条语句
  9. 负载均衡轮询算法和服务器性能,SpringCloud-Ribbon负载均衡机制、手写轮询算法
  10. linux shutdown 命令
  11. quartus波形仿真破解MODELSIM
  12. 私活之安卓论坛Demo
  13. excel两个表格数据对比_Excel表格中数据比对和查找的几种技巧
  14. 什么是 Hash 冲突?如何解决 Hash 冲突?
  15. 三维重建笔记——Linux环境下openMVG的安装
  16. android 手机震动1次,Android的手机震动
  17. NYOJ 1238 最少换乘
  18. 1367:查找二叉树(tree_a)
  19. 记录一个因为使用masonry 布局造成的宽度不准确的问题
  20. php中使用soap的建立共享接口

热门文章

  1. ArrayList源码剖析
  2. Percona PT-kill重构版(PHP)
  3. 原生ES-Module在浏览器中的尝试
  4. 机器学习: 最大似然估计 (MLE) 最大后验概率(MAP)
  5. linux 下svn安装
  6. 微信小程序电商实战-首页(下)
  7. php mpm,Ubuntu Apache 切换到php-fpm+mpm_event模式
  8. 6.Entity FrameWork Core 5.0 删除、修改数据
  9. php留言板验证验证码,留言板7 图形验证码
  10. mysql 查询和修改组合_MySql语句大全:创建、授权、查询、修改等