您的位置:首页 > 编程语言 > C语言/C++

《深度探索C++对象模型》读书笔记之构造、析构、拷贝语意学

2014-08-15 01:11 337 查看
1、 继承体系下的对象构造过程是怎样的?
1> 调用所有的虚基类构造函数,从左到右,由最深到最浅(别忘记虚基类在对象模型中是以独特的方式支持的,不涉及到在对象模型中的偏移量的问题)
2> 调用所有的上一层的基类构造函数,以基类的声明顺序为顺序(这是因为一般基类的subobject都会被放在object的开始,并且按基类声明的次序放置)
3> 如果class object有虚函数表指针,设定其初值,指向适当的虚函数表(开始进入派生类由编译器安插的数据成员的构造咯)
4> 如果又一个member并没有出现在成员初始化列表中,且它有一个默认构造函数,那么该默认构造函数必须被调用
5> 记录在成员初始化列表中的数据成员初始化操作会被放在constructor的函数本身,并以members声明的顺序为顺序
6> 程序员自己的代码(在此步以上的操作均为编译器安插的)

2、 继承体系下的对象构造过程中如何压制虚基类的构造函数的重复调用?
“virtual base class constructors的调用”有着明确的定义:只有一个完整的classobject被定义出来时,它才会被调用;如果object只是某个完整的object的subject,它就不会被调用。


由继承体系下的对象模型可知,当有Vertex3d的对象声明时,首先调用Point部分的构造函数,由于是完整的class object被定义出来,所以调用成功。当调用Point3d或者Vertex的构造函数时,其内部对Point的构造函数的调用将会被抑制,因为Point3d或Vertex只是完整object的subobject。

一. 构造函数

1. 构造函数是一种用于创建对象的特殊成员函数,当创建对象时,系统自动调用构造函数。

2. 构造函数名与类名相同,一个类可以拥有多个构造函数(重载),构造函数可以有任意类型的参数,但不能具有返回类型,连Void也不可以,它有隐含的返回值,该值由系统内部使用。

3. 构造函数的作用是:为对象分配空间;对数据成员赋初值;请求其他资源。

4. 如果一个类没有定义构造函数,编译器会自动生成一个无参的默认构造函数。

二. 析构函数:

1. 析构函数名字为符号“~”加类名,析构函数没有参数和返回值。

2. 一个类中只可能定义一个析构函数,所以析构函数不能重载。

3. 析构函数的作用是进行清除对象,释放内存等。当对象超出其定义范围时(即释放该对象时),编译器自动调用析构函数。

在以下情况下,析构函数也会被自动调用:

(1)如果一个对象被定义在一个函数体内,则当这个函数结束时,该对象的析构函数被自动调用。

(2)若一个对象是使用new运算符动态创建的,在使用delete运算符释放它时,delete将会自动调用析构函数。用new创建的对象,必须用delete销毁。

4. delete释放new产生的空间,而delete [] 释放new []产生的数组空间。

例如:

class A

{

A(){ m_pstr = new char[10];}

~A(){ delete[] m_pstr;m_pstr = NULL;}

}

A *pObj = new A[10];

三. 拷贝构造函数:

1. 当构造函数的参数为自身类的引用时,这个构造函数称为拷贝构造函数。拷贝构造函数的功能是用一个已有对象初始化一个正在建立的同类对象。

2. 拷贝构造函数的特点如下:

(1)该函数名与类同名,因为它也是一种构造函数,并且该函数也不被指定返回类型;

(2)该函数只有一个参数,并且是对某个对象的引用;

(3)每个类都必须有一个拷贝构造函数;

(4)如果程序员没有显式地定义一个拷贝构造函数,那么,C++编译器会自动生成一个缺省的拷贝构造函数.

(5)拷贝构造函数的目的是建立一个新的对象实体,所以,一定要保有证新创建的对象有着独立的内存空间,而不是与先前的对象共用。

在定义一些类时,有时需要(而且强立推荐)显式地定义拷贝构造函数。

3.拷贝构造函数主要在如下三种情况中起初始化作用。

(1)声明语句中用一个对象初始化另一个对象(一定是在"声明并初始化对象"时被调用);例如TPoint P2(P1)表示由对象P1初始化P2时,需要调用拷贝构造函数。

(2)将一个对象作为参数按值调用方式(而不是指针)传递给另一个对象时生成对象副本(即 复制构造函数在"对象作为函数参数"时被调用)。当对象作为函数实参传递给函数形参时,如p=f(N),在调用f()函数时,对象N是实参,要用它来初始化被调用函数的形参,这时需要调用拷贝构造函数。

(3)生成一个临时的对象作为函数的返回结果。但对象作为函数返回值时,如return R时,系统将用对象R来初始化一个匿名对象,这时需要调用拷贝构造函数。

4.拷贝构造函数的执行:

(1)用已有对象初始化创建对象。

(2)当对象作函数参数时,因为要用实参初始化形参,也要调用拷贝构造函数。

(3)函数返回值为类类型时,情况也类似。

四. 赋值函数:

1. 赋值操作符则给对象一个新的值,既然是新的值,说明这个对象原来就有值,这也说明,赋值函数只能被已经存在了的对象调用。

2. 如果不主动编写拷贝构造函数和赋值函数,编译器将以“位拷贝”的方式自动生成缺省的函数。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。

五. 成员初始化的必要性(摘自<<C++程序设计语言(特别版)>>第221页,略作修改).

1. 对于那些初始化与赋值不同的情况,主要有以下两种情况:1) 没有默认构造函数的类的成员对象; 2)const 成员和引用成员,对成员的初始式(即初始化列表)是必不可少的。例如:

class A {

string name;

Date founded;

A(const string& n, Date fd);

};

class X {

const int i; // const 成员

A a; // 没有默认构造函数的类的成员对象

A& pa; // 引用成员

X(int ii, const string& n, Date d, A& a): i(ii), a(n, d), pa(a) { };

不存在对这些成员做初始化的其它方式,而且,不对这种成员初始化就是错误。

除了以上的两种情况必须采用初始式之外,采用初始式(初始化列表)还有以下两大优点:

(1) 这种形式使得初始化过程更加明显;

(2) 会带来效率上的优势。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐