第五章、构造、析构、拷贝语意学

class Abstract_base{
public:
    virtual
~Abstract_base()=0;//有问题,链接不通过,不能是纯虚函数
    virtual void
interface() const =0;//如果改到类的成员变量,最好不要设置为const
   
//下面函数很糟糕,因为是virtual,函数定义内容不与类型有关,因而几乎不会被后继的derived
class改写
    virtual const char*
mumble()const
    {return
_mumble;}
protected:
//最好提供构造函数
//Abstract_base(char
*mumble_value=0):_mumble(mumble_value){}
    char
*_mumble;
};
class Concrete_derived:public
Abstract_base{
public:
    void
interface();
    Concrete_derived();
};

抽象类中可以有非静态的成员变量,此时要在抽象类的构造函数中用初始化列表来初始化它

析构函数不能是(最好不是)纯虚函数,因为每个派生类析构函数会被扩展,以静态调用的方式调用每一个virtual
base以及上一层的destructor,缺少任何一个base class
destructor就会导致链接失败,而此时如果此时是纯虚函数,则不可能有析构函数来调用。
class
TESTA
{
public:
virtual ~TESTA()=0;
};

class TESTB:public TESTA
{
public:

~TESTB(){};
};
调用时 TESTB b;在VS2012中是会出错的。

最上面的类重新设置:
class Abstract_base{
public:
    virtual
~Abstract_base();
    virtual void interface()
=0;//不再是const
    //不再是virtual
    const
char* mumble()const
    {return
_mumble;}
protected:
    Abstract_base(char
*mumble_value=0):_mumble(mumble_value){}
    char
*_mumble;
};

1.无继承情况下的对象构造
貌似和前面的章节重复了,忽略

2.继承情况下的对象构造
①记录在member initialization list的data
members初始化操作会被放进constructor的函数本身,并以members的声明顺序为顺序。
②如果有一个member没有出现在member
initialization
list之中,但他有一个默认构造函数,则该默认构造函数被调用。
③如果有虚表,则要设定虚指针
④上一次的基类构造函数必须要调用:如果基类在member
initialization list中,那么明确的参数都应该传递过去;如果没有在
member initialization
list之中,那么就调用它的默认构造函数。
5所有虚基类构造函数都要被调用,从左到右,从深到浅。

对于虚拟继承,它在构造函数添加一个测试参数来决定是否要调用虚基类的构造函数。
基类Point有两个派生虚继承Point3d,Vertex,而Vertex3d又继承于Point3d,Vertex,最后pVertex继承于Vertex3d
那么可以改写:
Point3d的构造函数为(Point3d
*this ,bool __most_derived,float x,float y,float
z)只要__most_derived为真时才会调用Point的构造函数。

vptr初始化语意学
如果基类Point有两个派生虚继承Point3d,Vertex,而Vertex3d又继承于Point3d,Vertex,最后pVertex继承于Vertex3d。其中每个类中都要一个虚函数virtual
function size()返回该类的大小,如果在各个类的构造函数中调用会直接返回该类的大小;如果在类外使用,则根据具体值来决定。
pVertex
pv;
Point3d p3d;
Point
*pt=&pv;
pt->size();//返回pVertex大小
pt=&p3d;pt->size();//返回Point3d的大小
但是如果在构造函数中就只能是该类的大小。

构造函数的调用顺序:
①所有虚基类和基类的构造函数会被调用
②vptr被初始化,指向相关的irtual table
③member
initialization list在构造函数展开并在最前面
④执行构造函数的程序员的代码

所以说在member initialization list中调用该类的虚拟函数一般是安全的,除非该虚拟函数用到member initialization
list中初始化的变量。

3.对象复制语意学
对象赋值(拷贝)函数是为了打开named value return(NVR)
对象赋值操作copy assignment
operator的合成条件和构造函数类似
当不要Bitwise Copy
Semantics时,类就需要合成一个对象赋值操作:
1.当class内含一个member object,而member object声明有一个copy
constructor operator时
2.当class继承一个base class而后者存在有一个copy constructor
operator时
3.当class声明了一个或多个virtual
functions时,
4.当class派生自一个继承串链,其中有一个或多个virtual base classes时。此时无论基类有没有copy
operator。

4.性能比较

5.析构语意学
如果类没有定义析构函数,那么只有在类内袋的成员对象(或是自己的基类)拥有destructor的情况下,编译器才会自动合成出一个来。其他情况不会合成,比如说是指针。
它的执行顺序:
①析构函数本身执行
②如果类有成员对象,则按照声明顺序相反调用它们虚构函数
③如果有vptr,则重新设定,指向适当的base
class virtual
class
④如果有上一层的非虚继承的基类有析构函数,他们会议声明的顺序相反调用析构函数(在多重继承下从右到左)。
如果有基虚类的析构函数,而当前讨论的这个class是最尾端的class,那么他们会议原来的构造顺序的相反顺序被调用。

时间: 2024-10-06 00:01:03

第五章、构造、析构、拷贝语意学的相关文章

深度探索C++对象模型 第五章 构造、析构、拷贝语意学

1. const 成员函数需要吗? 尽量不要,如果存在继承,则无法预支子类是否有可能改变data member 2. pure virtual constructor 可以实现类的隐藏吗(包含data member)?   这样子类无法调用base 的构造函数对数据初始化,所以可以用protected来实现构造函数,可以实现子类调用: 3. 如果class中存在virtual function,则编译器会再构造函数中对vptr进行初始化(在base构造函数调用之后,而代码实现之前) 4.拷贝构造

《Effective C++》第2章 构造/析构/赋值运算(1)-读书笔记

章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effective C++>第8章 定制new和delete-读书笔记 条款05:了解C++默默编写并调用哪些函数 当C++处理过一个空类后,编译器就会为其声明(编译器版本的):一个拷贝构造函数.一个拷贝赋值运算符和一个析构函数.如果你没有声明任何构造函数,编译器还会声明一个默认构造函数.所有这些函数都被声明为pub

《Effective C++》第2章 构造/析构/赋值运算(2)-读书笔记

章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(2)-读书笔记 <Effective C++>第8章 定制new和delete-读书笔记 条款09:绝不在构造和析构过程中调用virtual函数 你不该在构造和析构函数期间调用virtual函数,因为这样的调用不会带来你预期的结果. (1)在der

C++对象模型——构造,解构,拷贝语意学(第五章)

第5章 构造,解构,拷贝语意学 (Semantics of Construction, Destruction, and Copy) 考虑下面这个abstract base class 声明: class Abstract_base { public: virtual ~Abstract_base() = 0; virtual void interface() const = 0; virtual const char * mumble() const { return _mumble; } p

构造、解构、拷贝语意学

一 "无继承"情况下的对象构造 考虑下面程序片段: 1 2 3 4 5 6 7 8 9 10 11 Point glocal; //全局内存配置 Point foobar() {    Point local;//局部栈内存配置    Point *heap=new Point;//heap内存配置    *heap=local;        delete heap;    return local; } 1 把Point类写成c程序,c++标准说这是一种所谓的Plain OI Da

c++第五章-(类与对象、构造器和析构器)

1.构造器与结构体的区别:构造器多支持方法.其作用有申请内存,初始化变量. 在c++中构造器没有返回值:语法Class Name(); 2.析构器的作用:释放内存. 在c++中析构器没有返回值:语法~ClassName(); class Animal { public: std::string mouth; std::string name; void eat(); void sleep(); void drool(); Animal(std::string theName); }; class

【软件构造】第五章第二节 设计可复用的软件

第五章第二节  设计可复用的软件 5-1节学习了可复用的层次.形态.表现:本节从类.API.框架三个层面学习如何设计可复用软件实体的具体技术. Outline 设计可复用的类--LSP 行为子结构 Liskov替换原则(LSP) 各种应用中的LSP 数组是协变的 泛型中的LSP 为了解决类型擦除的问题-----Wildcards(通配符) 设计可复用的类--委派与组合 设计可复用库与框架 Notes ## 设计可复用的类--LSP 在OOP之中设计可复用的类 封装和信息隐藏 继承和重写 多态.子

软件构造 第五章第一节 可复用性的度量、形态和外部观察

第五章第一节  可复用性的度量.形态和外部观察 面向复用编程(programming for reuse):开发出可复用的软件 基于复用编程(programming with reuse):利用已有的可复用软件搭建应用系统 代码复用的类型: 白盒复用:源代码可见,可修改和扩展 含义:复制已有代码到正在开发的系统,进行修改 优点:可订制化程度高 缺点:对其修改增加了软件的复杂度,且需要对其内部充分的了解 黑盒服用:源代码不可见,不能修改 含义:只能通过过API接口来使用,无法修改代码 优点:清晰.

第五章简易笔记

第五章-继承 1. 继承已存在的类就是复用(继承)这些类的方法和域.在此基础上,还可以添加一些新的方法和域,以满足新的需求. 2.反射是指在程序运行期间发现更多的类及其属性的能力. 3.在java中所有继承都是公有继承. 4. 在子类中可以增加域.增加方法或覆盖超类的方法,然而绝对不能删除继承的任何域和方法. 5.this与super关键字用途总结: this用途: (1)引用隐式参数: (2)调用该类其他的构造器. super用途: (1)调用超类方法: (2)调用超类构造器. 6.调用构造器

Thinking In Java笔记(第五章 初始化与清理(二))

第五章 初始化与清理(二) 5.5 清理:终结处理和垃圾回收 清理的工作常常被忽略,Java有垃圾回收器负责回收无用对象占据的内存资源.但也有特殊情况:假定对象(并非使用new)获得了一块"特殊"的内存区域,由于垃圾回收器只知道释放那些由new分配的内存,所以不知道如何释放特殊内存.Java允许在类中定义一个名为finalize()的方法,工作原理"假定"是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,首先调用其finalize()方法,并且在下一次垃圾回收动