|
|
|
《面向对象程序设计》自学方法指导 |
|
|
来源: 发布时间:2006-5-29 17:04:30 点击: |
构造函数:构造函数是一种特殊的成员函数,它主要用来为对象分配空间,给它的数据成员赋值(初始化),并执行对象的其他内部管理操作。构造函数的名字与它所在的类名相同,它可以接收参数,并允许重载。当一个类中含有多个构造函数时,编译程序为了确定调用哪一个构造函数,需要把对象中使用的参数与构造函数的参数表进行比较,这个过程与在普通重载函数之间进行选择的过程是一样的。关于构造函数的定义和执行过程,可参照例3.2。书上讲得过于简单,大家在阅读时可能有些地方不是很了解,下面我们通过一个更具体的例子来说明构造函数是如何给类赋初值的。 下面是一个定义了构造函数的类: class stack { private: char *v; char *p; int size; public: stack() //缺省构造函数 {size=100; v=new char[100]; //new 动态分配一大小为100的字符数组,p指向数组中的第一个元素的地址 p=v; }//带参构造函数 stack(int sz) //stack 的构造函数 { p=new char[size = sz]; //new 动态分配一大小为sz的字符数组,p指向数组中的第一个元素的地址 v=p; } }; 注意:如果想创建类的实例,应把构造函数作为公有成员函数,否则,无法创建对象。上面定义的类名为stack,于是构造函数名也是stack,该函数用来给对象赋初值。构造函数是在定义对象的同时调用的。其一般格式为: 类名 对象名(实参表); 这里的"类名"与构造函数名相同,"实参表"是为构造函数提供的实际参数。当我们定义它的对象时,采用类似于函数调用的标准语法将参数值传到构造函数: stack sta1(100); 它创建了一个类的实例,即先为sta1对象分配空间,然后调用stack的构造函数,把100作为参数传给此函数,因此sta1对象中,v数组的大小为100。如果某个类定义中没有构造函数,编译器就会自动生成一个缺省构造函数(不带有参数的构造函数叫缺省构造函数,缺省的构造函数初始化数据成员时一般给它们赋缺省值)。但这种编译器生成的构造函数不会给类的数据成员赋初值。因此,如果想显示初始化数据成员或实施某种其他初始化任务时,必须自己定义构造函数。一旦定义了自己的构造函数,编译器便不会再生成缺省构造函数,在每次定义实例时,都要用到自定义构造函数来初始化。因此,如果当类中只定义了带参构造函数stack(int sz)时,如果再用: stack sta2; //出错 就会出错。因为编译器找不到缺省的构造函数来初始化它(用户没有定义,编译器也不会自己生成)。 应注意,缺省构造函数和带参构造函数在定义对象时使用不同的语法形式,不要在缺省构造函数之后加上空括号,如果这样做了,实际声明的是一函数,其返回类型是此类,并没定义类的实例: stack sta3(); //声明了一个无参函数 //返回一个stack对象 如果出了这种错,编译器并不会产生错误,直到把sta3作为实例使用时才出现矛盾,这样会在我们调试程序时误导我们,产生不必要的麻烦。 另外,在使用重载构造函数时应注意不要产生二义性,现在重写stack 类中的带参构造函数并为它赋予一缺省值: stack(int sz=70) { …… } 把这个定义放入类stack的public段后,再按下面的形式使用缺省构造函数: stack sta4; //二义性,出错 就会出错,因为在初始化sta4时,编译器无法确定是使用缺省构造函数,还是使用具有带有缺省参数的带参构造函数。 特别要说明的是,构造函数没有返回值! 析构函数 析构函数也是类中的特殊成员函数,与定义它的类具有相同的名字,但要在前面加上一个波浪号(~)。它没有参数,也没有返回值,而且不能重裁,即在一个类中只能有一个析构函数。析构函数执行与构造函数相反的操作,通常用于释放分配给对象的存储空间。当程序超出类对象的作用域时,或者当对一个类指针使用运算符delete时,将自动执行析构函数。和构造函数一样,如果不定义析构函数,编译程序将产生一个缺省的析构函数。对于大多数类来说,缺省的析构函数就能满足要求。如果在一个对象完成其操作之前还需要做一些内部处理,则应显式地定义析构函数。例如我们在上面举的那个例子,栈中的数组是用new动态创建的,它只能用delete进行显式删除。在局部变量出作用域时,不会自动释放其内存。因而,在撤销对象时,要调用析构函数来释放所分配的空间。即如下所定义: ~stack() { delete[ ]v; } 它用delete显式的删除了用new分配的数组v。 下面我们讨论构造函数和析构函数的调用时间。 一般来说,每当创建一个对象就要调用构造函数;每当撤销一个对象就要调用析构函数。对于某些有特点的对象类型,其构造函数和析构函数的调用时间,有如下的说明: ◇对于某些全局定义的对象(就是说定义在所有函数之外),每当程序第一次运行,主函数main接受控制之前,就要调用构造函数,整个程序结束时调用析构函数。 ◇对于局部定义的对象(也就是在一个函数内),每当程序控制流到达该对象处则调用构造函数;每当程序控制流走出定义该对象的程序块则调用析构函数(也就是对象出了作用域)。 ◇对于用new运算符动态创建的对象,每当创建该对象时,调用构造函数;每当用delete运算符显式的撤销对象时,调用析构函数(如程序员不显式撤销该对象,是不会调用析构函数的)。 6.继承和派生 继承既是类型扩充机制,又是模块扩充机制。利用继承很容易在原有类的基础上增加新的东西。同时继承也反映了特殊与普通之间的关系,因而,用它可以模拟客观现实世界。在C++中,继承时用派生类实现的。 派生类也称子类,是C++提供继承的基础,也是对原有的类进行剪裁的一种基本手段。派生类继承或修改了原有类中的部分或全部方法,而且可以增加原有类中没有的新方法,以满足派生类的需要。 被继承的类成为基类(base class),基类和派生类(derived class)分别称为父类和子类。派生类从基类中继承所有的公共部分,并可以增加数据成员和成员函数,这是的程序员可以根据基类与派生类之间的差异来建立指定对象的新类,对相同部分的代码不必重新定义。此外,还可以为多个不同的类提供公共的界面,使编程人员更容易表达类型间的关系,从而可以减少程序设计的工作量。 定义派生类的一般格式如下: class 派生类名:[访问属性]基类名…… 其中class是关键字,"派生类名"是新定义的类名,"访问属性"是访问说明符,即private,public和protected。派生类缺省为private,派生类名与访问属性之间用冒号隔开,基类名可以有一个,也可以有多个,有一个的是简单继承,有多个的叫多重继承,每个基类名之间用逗号隔开。 派生类的访问控制由访问属性来确定,它按下属方式来继承基类的访问属性: ◇public 基类名。基类的public成员是派生类的public成员;基类的protected成员是派生类的protected成员;基类的private成员对基类仍然保持private属性。 ◇protected基类名。基类的public和protected成员是派生类的protected成员,基类的private成员对基类仍然保持private属性。具体来说.基类定义中声明为protected的数据只能被基类的成员函数及其派生类的成员函数访问;不能被派生类以外的成员函数访问。 ◇private 基类名。基类的public和protected成员都是派生类的private成员,基类的private成员对基类仍然保持private属性。也就是说,当访问属性为private时,派生类的对象不能访问基类中任何方式定义的成员函数。 可以看出,派生类继承的是基类的公有部分。为了保持类的数据隐藏特性、不允许派生类自动地继承基类的私有部分,只有在特别授权的情况下才能访问基类的私有部分。这种授权有以下两种方式: |
|
|
|