1、C+培训教程,医疗IT事业部 演讲人,目录,一、C+语言概述 二、类和对象 三、继承与派生 四、多态性 五、总结,一、C+语言概述,C+语言的特点 C+语言对C语言扩充和增强的具体体现 C+的优点,C+语言的特点,全面兼容C它保持了C的简洁、高效和接近汇编语言等特点;对C的类型系统进行了改革和扩充;C+也支持面向过程的程序设计,不是一个纯正的面向对象的语言; 支持面向对象的方法,C+语言对C语言扩充和增强的具体体现,注释 在C语言块注释的形式/*Explanation Sentence*/的基础上,C语言提供了一种新的单行注释形式: /Explanation Sentence,即用“/”表示注
2、释开始,从该位置直到当前行结束的所有字符都被作为注释。 更加灵活的变量说明 在传统的C语言中,局部变量的说明必须集中放在执行代码的前面,数据说明语句和执行语句的混合将引起编译错误。而在C+中,可以在程序代码块的任何地方进行局部变量的说明。比如下面的代码在C语言中是不正确的,在C+语言中却可以正常运行。for(int i = 1; i = 100; i+);,C+语言对C语言扩充和增强的具体体现,更加严格的函数原型说明 C+摒弃了C语言对函数原型随意简化的方式,C+语言要求编程者为函数提供完整的原型,包括全部参数的类型和返回值得说明。 例如,有字符型和双精度类型两个参数、返回整型值的函数f,原型
3、应该写为:int f(char, double); 而C语言中允许将这个原型写成“f( );“。在函数原型说明中,参数名可有可无,并且可以和函数定义中的参数名不一致。,C+语言对C语言扩充和增强的具体体现,增加了函数重载机制 重载是程序语言领域的重要概念。常规语言中最典型的例子是“+、/“等各种算术运算符的重载,这些符号可以同时用来表示多种类型数据之间的运算,这种对一个名字或一个符号赋予多重意义的情况就叫重载。 C+语言增加了C语言所没有的函数重载机制。对一个函数名可以给出多个函数定义,只要这些定义可以通过参数个数或类型的不同区别开来即可。 C+还允许对系统中预先定义的运算符号进行重载,增加新
4、的定义。这样做的优点是在今后对新定义类型的变量进行运算时,计算公式写起来方便自然。,C+语言对C语言扩充和增强的具体体现,函数缺省参数 C+中允许函数有缺省参数。所谓缺省,是指函数调用时可以不给出实际的参数值。有缺省参数的函数定义的实例: int f(int a, int b=1) return a*b; 此后,函数调用f(3,1)和f(3)将返回同样的结果。 更加方便的动态存储分配 C+为了提高内存管理上的灵活性,提供了动态内存分配合释放的操作符new和delete,用来增强C语言中原有的函数malloc()和free();,C+语言对C语言扩充和增强的具体体现,增加了内联函数(Inline
5、 Function) C+提供了内联函数,用以代替C语言中的宏。宏的处理机构是预处理器而不是编译器,它虽然可以提高效率,但是却不能实现函数调用所拥有的参数类型检查等机制。内联函数不但能够象宏那样节约函数调用时保存现场所需的系统开销,提高程序执行效率,还保留了函数进行参数类型检查的机制;并且C+语言中的宏是不能够存取对象私有成员变量的,但是使用内联函数,则没有这一限制。,C+语言对C语言扩充和增强的具体体现,输入/输出流机制 C保留了C语言标准库中各种输入/输出函数,而且提供了一套新的输入/输出机制流机制。 比如向标准输出输出一个字符串: couta; 流式输入/输出运算符能够根据变量类型自动确
6、定数据交换过程中的转换方式,还可以定义“的重载,方便了编程者自定义类型的数据的输入/输出。,C+语言对C语言扩充和增强的具体体现,作用域限定运算符: 作用域限定运算符:,用于对当前作用域之外的同名变量进行访问。例如在下面的例子中,我们可以利用:实现在局部变量a的作用域范围内对全局变量a的访问。 #include int a; void main() float a; a = 3.14; :a = 6; cout“local variable a = “aendl; cout“global variable a =“:aendl; 程序执行结果如下: local variable a = 3.1
7、4global variable a = 6,C+的优点,与C语言兼容,既支持面向对象的程序设计,也支持结构化的程序设计。同时,熟悉C语言的程序员,能够迅速掌握C+语言。 修补了C语言中的一些漏洞,提供更好的类型检查和编译时的分析。使得程序员在C+环境下继续写C代码,也能得到直接的好处。 生成目标程序质量高,程序执行效率高。一般来说,用面向对象的C+编写的程序执行速度与C语言程序不相上下。,C+的优点,提供了异常处理机制,简化了程序的出错处理。利用throw、try和catch关键字,出错处理程序不必与正常的代码紧密结合,提高了程序的可靠性和可读性。 函数可以重载及可以使用缺省参数。重载允许相
8、同的函数名具有不同参数表,系统根据参数的个数和类型匹配相应的函数。缺省参数可以使得程序员能够以不同的方法调用同一个函数,并自动对某些缺省参数提供缺省值。,C+的优点,提供了模板机制。模板包括类模板和函数模板两种,它们将数据类型作为参数。对于具体数据类型,编译器自动生成模板类或模板函数,它提供了源代码复用的一种手段。,二、类和对象,类与对象概述 构造函数与析构函数 拷贝构造函数 类作用域 const成员函数 静态成员,类与对象概述,类是具有相同属性和行为的一组对象的集合,它为属于该类的全部对象提供了统一的抽象描述,其内部包括属性和行为两个部分。 利用类可以实现数据的封装、隐藏、继承与派生。 利用
9、类易于编写大型复杂程序,其模块化程度比C中采用函数更高。,类与对象概述,类的声明形式 class 类名称 public:公有成员(外部接口)private:私有成员protected:保护型成员 ;,程序模型则是若干个对象的相互作用一堆“鸡蛋”,类与对象概述,公有属性成员在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有属性的数据和函数。通俗讲是“谁都可以使用”。 私有属性成员在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。通俗讲是“谁都不可以使用,只供我自己用”。 保护属性成员在关键字protected后面声明,允许本类及子类
10、中的函数访问,通俗讲是“我的子孙可以使用,外人不可用”。,类与对象概述,类的成员,类与对象概述,成员数据 与一般的变量声明相同,但需要将它放在类的声明体中。 可放置在类中的任何位置; 不可声明时初始化; 不可声明为extern、register,但可以是static 或const。,类与对象概述,成员函数 可以仅在类中说明原型,而在类外给出函数体实现,并在函数名前使用类名加以限定。 也可以直接在类中给出函数体,形成成员函数的隐含内联。 允许成员函数为重载函数和带默认形参值的函数。 对成员数据的使用不再遵循“先声明后使用”的原则,即可以放置在类中任意位置。 凡被调用的成员函数一定要有函数实现。,
11、类与对象概述,内联成员函数 为了提高运行时的效率,对于较简单的函数可以声明为内联形式。 内联函数体中不要含有复杂结构(如循环语句和switch语句)。 在类中声明内联成员函数的方式:将函数体放在类的声明中隐式声明。使用inline关键字显式声明。,类与对象概述,对象类的对象是该类的某一特定实体,即类类型的变量。定义对象时,系统会为每一个对象分配自己的存储空间,该空间只保存数据,函数代码是所有对象共享的。声明形式:类名 对象名;例:Clock myClock;,构造函数与析构函数,构造函数的作用是在对象被创建时使用特定的值构造对象,或者说将对象初始化为一个特定的状态。 初始化是指对象(变量)诞生
12、的那一刻即得到原始值的过程。是动词。对象声明然后赋值,经历了状态由不确定到确定的过程。,构造函数与析构函数,构造函数的特点: 与类同名; 无返回类型; 通常是公有成员; 在对象创建时由系统自动调用,亦可由程序员显式调用,但不可用对象调用; 如果程序中未声明,则系统自动产生出一个默认形式的构造函数,它无参,函数体为空; 允许为内联函数、重载函数、带默认形参值的函数; 是可以使用初始化列表的两函数之一(特权); 不可以是常函数,亦不可是虚函数; 天然具有类型转换功能,除非用explicit关闭。,构造函数与析构函数,构造函数举例 class Clock public:Clock (int NewH
13、, int NewM, int NewS); /构造函数void SetTime(int NewH, int NewM, int NewS);void ShowTime();private:int Hour, Minute, Second; ;,构造函数与析构函数,构造函数的实现: Clock:Clock(int NewH, int NewM, int NewS) Hour= NewH;Minute= NewM;Second= NewS; void main() Clock c (0,0,0); /隐含调用构造函数,将初始值作为实参。c.ShowTime(); ,构造函数与析构函数,构造函数显
14、式调用 #include class tree /定义类tree public:tree(int i ):ages(i) /构造函数void grow(int years); /计算树的年龄ages += years;void age(); /显示树的年龄cout“树的年龄为: “agesendl; private:int ages; /私有数据成员 ;,构造函数与析构函数,构造函数显式调用 void main() int age,year;coutage;tree pTree = new tree(age); /显式调用产生无名对象pTree-age(); /显示树的年龄coutyear;p
15、Tree- grow(year); /计算树的年龄pTree- age(); /显示树的年龄 ,构造函数与析构函数,析构函数 完成对象被删除前的一些清理工作(恰与构造函数对称)。 在对象的生存期结束的时刻系统自动调用它,然后再释放此对象所属的空间。 如果程序中未声明析构函数,编译器将自动产生一个默认的析构函数。 不接受任何参数,亦无法重载。,构造函数与析构函数,析构函数的调用时机 或文件尾; delete (有条件地); catch() 当中途退出程序时(exit()、abort()不调用析构函数,构造函数与析构函数,构造函数和析构函数举例 #include using namespace s
16、td; Class Point public:Point(int xx, int yy);Point();/.其它函数原型 private:int X, int Y; ;,构造函数与析构函数,构造函数和析构函数举例 Point:Point(int xx, int yy) X=xx; Y=yy; Point:Point() /.其它函数的实现略,拷贝构造函数,拷贝构造函数是一种特殊的构造函数,其形参为本类的对象引用 class 类名 public :类名(形参);/构造函数类名(类名&对象名);/拷贝构造函数原型. ; 类名: 类名(类名&对象名)/函数的实现 函数体 ,拷贝构造函数,拷贝构造函
17、数定义 class Point public:Point(int xx=0,int yy=0)X=xx; Y=yy;Point(Point,拷贝构造函数,拷贝构造函数定义 Point:Point(Point ,拷贝构造函数,拷贝构造函数调用 当用类的一个对象去初始化该类的另一个对象时系统自动调 用拷贝构造函数实现拷贝赋值。void main(void) Point A(1,2);Point B(A); /拷贝构造函数被调用cout B.GetX() endl; ,拷贝构造函数,拷贝构造函数调用 若函数的形参为类对象,调用函数时,实参赋值给形参, 系统自动调用拷贝构造函数。例如: void fu
18、n1(Point p) coutp.GetX()endl; void main() Point A(1,2);fun1(A); /调用拷贝构造函数 ,拷贝构造函数,拷贝构造函数调用 当函数的返回值是类对象时,系统自动调用拷贝构造 函数。例如: Point fun2() Point A(1,2);/调用拷贝构造函数return A; void main() Point B;B=fun2(); ,类作用域,类本身可被定义在三种作用域内: 全局作用域。这是所谓全局类,绝大多数的C+类是定义在该作用域中,我们在前面定义的所有类都是在全局作用域中。 在另一个类的作用域中。这是所谓嵌套类,即一个类包含在另
19、一个类中。 在一个块的局部作用域中。这是所谓局部类,该类完全被块包含。,类作用域,嵌套类的例子 class Rectangle public:Rectangle (int, int, int, int); private:class Point public: Point (int, int); private: int x, y; ; Point topLeft, botRight; ;,类Point嵌套在Rectangle类中,Point成员函数可定义为内联的,也可在全局作用域中,但后者对成员函数名需要更多的限制,例如: Rectangle:Point:Point (int x, int
20、y) /. 同样,在类域外访问嵌套类需要限制类名,例如: Rectangle:Point pt(1,1);,类作用域,局部类的例子 void Render (Image /. ,类ColorTable是在函数Render中的局部类。与嵌套类不同的是:局部类不能在其局部作用域外访问,例如:ColorTable ct; / 非法,没有定义ColorTable类!局部类必须完全定义在局部作用域内。所以,它的所有成员函数必须是内联的,这就决定了局部类的成员函数都是很简单的。,const成员函数,const成员函数定义 class Point public:int GetX() const;int Ge
21、tY() const;void SetPt (int, int); void OffsetPt (int, int); private: int xVal, yVal; ;,int Point:GetY() const return yVal; const成员函数应该在函数原型说明和函数定义中都增加const限定 。,const成员函数,class Set public:Set (void) card = 0; bool Member(const int) const; void AddElem(const int); /. ; bool Set:Member (const int elem)
22、 const /. ,非常量成员函数不能被常量成员对象调用,因为它可能企图修改常量的数据成员: const Set s; s.AddElem(10); / 非法: AddElem不是常量成员函数 s.Member(10); / 正确 但构造函数和析构函数对这个规则例外,它们从不定义为常量成员,但可被常量对象调用(被自动调用)。它们也能给常量的数据成员赋值。,静态成员,静态数据成员 要定义静态数据成员,只要在数据成员的定义前增加static关键字。静态数据成员不同于非静态的数据成员,一个类的静态数据成员仅创建和初始化一次,且在程序开始执行的时候创建,然后被该类的所有对象共享;而非静态的数据成员则
23、随着对象的创建而多次创建和初始化。,静态成员,静态数据成员定义的实例 class Test public:static int public_int; private: static int private_int; ; void main() Test:public_int = 145; / 正确 Test:private_int = 12; / 错误,不能访问私有的数据成员 从上例我们可以看到:静态数据成员的访问方式是:类名: 静态数据成员名。但是,不能直接访问私有的数据成员。,静态成员,私有的静态数据成员 class Directory public:. private: / 数据成员
24、static char path; ;,数据成员path是一个私有的静态变量,在程序执行过程中,仅一个Directory:path存在,即使有多个Directory类的对象。静态的数据成员能够被类的成员函数访问,但不能在构造函数中初始化。这是因为静态数据成员在构造函数被调用之前就已经存在了。静态数据成员可以在定义时初始化,且必须在类和所有的成员函数之外,与全局变量初始化的方法一样。,静态成员,私有的静态数据成员 类Directory的数据成员的定义 与初始化方法如下: / 静态数据成员的定义与初始化 char Directory:path 200 = “/usr/local”; / 无参的构造
25、函数 Directory:Directory() . ,在类中的静态数据成员的定义,只是说明该类有这么一个数据成员,并没有为该数据成员分配内存。就象非静态数据成员是在创建对象时分配内存一样,静态数据成员是在初始化时分配内存的。所以,在定义静态的数组成员时,可以省略它的尺寸(数组元素的个数),但在初始化时,必须确定数组的尺寸。,三、继承与派生,类的继承与派生 类成员的访问控制 单继承与多继承 派生类的构造、析构函数 类成员的标识与访问,类的继承与派生,类的继承与派生 保持已有类的特性而构造新类的过程称为继承。 在已有类的基础上新增一些特性而产生新类的过程称为派生。继承和派生叫法不同,是一件事物的
26、两个方面。 被继承的已有类称为基类(或父类)。 派生出的新类称为派生类(或子类)。,类的继承与派生,继承与派生问题举例,类的继承与派生,继承与派生的目的 继承的目的:实现代码重用。 派生的目的:当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程序进行改造。 继承为软件的层次化开发提供了保证。,类的继承与派生,派生类的声明 class 派生类名:继承方式 基类名 新增成员声明; ; 继承方式 三种继承方式公有继承public (原封不动)保护继承protected (折中)私有继承private (化公为私) 继承方式影响子类的访问权限派生类成员对基类成员的访问权限通过派生类对象
27、对基类成员的访问权限,类的继承与派生,继承方式与访问权限的区别 继承方式是表示类间关系,即从大的方面控制类的所有成员,表现了类的继承性。 访问权限仅是在类内控制其成员,表现了类的封装性。,类的继承与派生,继承的工作内容 吸收基类成员全盘(除了六个特别函数及静态外)接收;此项工作程序员无法干预。 改造基类成员 1.对基类成员访问权限的改变;(规则严格) 2.对基类成员的覆盖;(有技巧) 新增派生类特有的成员 “青出于蓝而胜于蓝”,类成员的访问控制,公有继承(public) 基类的public和protected成员的访问属性在派生类中保持不变,但基类的private成员不可直接访问。 派生类中的
28、成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。 通过派生类的对象只能访问基类的public成员。,类成员的访问控制,公有继承举例 class Point /基类Point类的声明 public:/公有函数成员void InitP(float xx = 0, float yy = 0)X = xx; Y = yy;void Move(float xOff, float yOff)X += xOff; Y += yOff;float GetX() return X;float GetY() return Y; private:/私有数据成
29、员float X,Y; ;,类成员的访问控制,公有继承举例 class Rectangle: public Point /派生类声明 public:/新增的公有函数成员void InitR(float x, float y, float w, float h)InitP(x, y); W = w; H = h;/调用基类公有成员函数float GetH() return H;float GetW() return W; private:/新增的私有数据成员float W,H; ;,类成员的访问控制,公有继承举例 #include #include using namespace std; vo
30、id main() Rectangle rect;rect.InitR(2, 3, 20, 10); rect.Move(3, 2); /直接调用基类的公有成员函数coutrect.GetX(), rect.GetY(),rect.GetH(),rect.GetW()endl; ,类成员的访问控制,私有继承(private) 基类的public和protected成员都以private身份出现在派生类中,但基类的private成员不可直接访问。 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。 通过派生类的对象不能直接访问基类
31、中的任何成员。,类成员的访问控制,私有继承举例 class Rectangle: private Point/派生类声明 public:/新增外部接口void InitR(float x, float y, float w, float h)InitP(x, y); W = w; H = h;/访问基类公有成员void Move(float xOff, float yOff) Point:Move(xOff, yOff); /子类要为基类提供访问方式float GetX() return Point:GetX();float GetY() return Point:GetY();float G
32、etH() return H;float GetW() return W; private:/新增私有数据float W,H; ;,类成员的访问控制,私有继承举例 #include #include using namespace std; int main() Rectangle rect;rect.InitR(2, 3, 20, 10);rect.Move(3, 2); /只能访问子类的方法coutrect.GetX(), rect.GetY(),rect.GetH(),rect.GetW()endl;return 0; ,类成员的访问控制,保护继承(protected) 基类的publi
33、c和protected成员都以protected身份出现在派生类中,但基类的private成员不可直接访问。 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。 通过派生类的对象不能直接访问基类中的任何成员,类成员的访问控制,保护继承举例 #include class base int x; protected:int y; public:base(int xx = 10, int yy = 20) x = xx; y = yy; void show() cout“基类的函数成员被调用! x= “x“ y= “yendlendl
34、; ;,类成员的访问控制,保护继承举例 class derived:protected base /保护派生类 int a; protected: int b; public:derived (int aa=50, int bb=60, int cc=70, int dd=80):base(cc,dd)a = aa; b = bb;void show()cout“派生类的函数成员被调用: “endl;cout“显示从基类继承的数据成员: “;base:show();cout“显示派生类自己的数据成员: a= “a“ b= “bendlendl; ;,类成员的访问控制,保护继承举例 void m
35、ain () base obj1(100,200);obj1.show();derived obj2(400,600); /仅对派生类自己的数据成员赋值obj2.show(); ,运行结果: 基类的函数成员被调用! x= 100 y=200 派生类的函数成员被调用: 显示从基类继承的数据成员: 基类的函数成员被调用! x= 70 y=80 显示派生类自己的数据成员: a= 400 b=600,类成员的访问控制,继承与静态成员 类的静态成员不参与继承,当然也不受继承方式的影响。 子孙们类都可访问基类的静态成员。 类外对静态成员的访问,取决于该成员的访问权限。,类成员的访问控制,继承与静态成员举例
36、 #include class Base /普通的基类Base public:Base(int a) x = a;void Setx(int a) x = a;static void Sety(int a) y = a; /静态函数成员void Show()cout“x = ”x“ y = ”yendl; private:int x;static int y;/静态数据成员 ;,类成员的访问控制,继承与静态成员举例 class Derived : public Base public:Derived(Base a, int b):Base(b),ob(a) void Show() /显示所继承
37、的数据成员及共享的静态成员Base:Show(); /显示了组合对象的数据成员及共享的静态成员 ob.Base:Show(); private:Base ob;/组合了基类的对象 ; int Base:y = 10;,类成员的访问控制,继承与静态成员举例 void main() Base A(99);A.Show();A.Setx(100);A.Sety(200);A.Show();Derived D(A,1234);D.Show(); ,运行结果: x= 99 y=10 x= 100 y=200 x=1234 y=200 x= 100 y=200,类成员的访问控制,三种继承方式的选择 若想完
38、全保留基类的操作功能,只是扩展新功能,则用公有继承。这叫“类型继承”。 若想完全改变基类的功能,将其改头换面,做成基于原来类,但隐藏、伪装了原功能,则用私有继承。只能算“实现继承”。 若想既隐藏基类的操作功能,又能方便的传给后代,不至于难以访问,则用保护继承。也是“实现继承”。,单继承与多继承,基类与派生类的关系 单继承派生类只从一个基类派生。 多继承派生类从多个基类派生。 多重派生由一个基类派生出多个不同的派生类。 多层派生派生类又作为基类,继续派生新的类。,单继承与多继承,单继承举例 class B public:. private:int b; ;,class D :public B p
39、ublic: . private:int d; ; D objd; B * pb= ,单继承与多继承,多继承时派生类的声明 class 派生类名:继承方式1 基类名1,继承方式2 基类名2,. 新增成员声明; ; 注意:每一个“继承方式”,只用于控制紧随其后之 基类的继承。 默认的继承方式是私有继承。,单继承与多继承,多继承举例 class A public:void setA(int);void showA( ); private:int a; ; class B public:void setB(int);void showB( );,private:int b; ; class C :
40、public A, private B public:void setC(int, int, int);void showC(); private:int c; ;,单继承与多继承,多继承举例 void A:setA(int x) a=x; void B:setB(int x) b=x; void C:setC(int x, int y, int z) /派生类成员直接访问基类的公有成员setA(x); setB(y); c=z; /其它函数实现略,void main() C obj;obj.setA(5);obj.showA();obj.setC(6,7,9);obj.showC();/ o
41、bj.setB(6); 错误/ obj.showB(); 错误 ,派生类的构造、析构函数,继承时的构造函数 基类的构造函数不被继承,派生类中需要重新定义自己的构造函数。 定义构造函数时,只需要对本类中新增成员进行初始化,继承来的基类成员的初始化工作由基类构造函数完成,基类构造函数是自动被调用的,但对有参的要用初始化表给出实参。 派生类的构造函数要用参数总表备齐实参,以便于传递给基类的构造函数所用。,派生类的构造、析构函数,单继承时构造函数的形式 派生类名:派生类名(基类所需的形参,本类成员所需的形参): 基类名(参数表) 本类成员赋初值语句; ,派生类的构造、析构函数,单继承时的构造函数举例
42、#include using namespace std; class B /声明父类 public:B();B(int i);B();void Print() const; private:int b; ;,/父类成员函数的实现,也叫“类实现”。 B:B() b=0;cout“Bs default constructor called.“endl; B:B(inti) b=i;cout“Bs constructor called.“ endl; B:B() cout“Bs destructor called.“endl; void B:Print() const coutbendl; ,派生
43、类的构造、析构函数,单继承时的构造函数举例 /声明子类 class C:public B public:C();C(int i,int j);C();void Print() const; private:int c; ;,派生类的构造、析构函数,单继承时的构造函数举例 C:C() c=0;cout“Cs default constructor called.“endl; C:C(int i,int j) : B(i) c=j;cout“Cs constructor called.“endl; ,C:C() cout“Cs destructor called.”endl; void C:Pri
44、nt() const B:Print();coutcendl; void main() C obj1(5,6); obj1.Print(); C obj2; obj2.Print(); ,派生类的构造、析构函数,派生类与基类构造函数的关系 当基类中未声明任何构造函数或声明了无参构造函数时,派生类构造函数可以不向基类构造函数传递参数。 若基类中未声明构造函数,派生类中也可以不声明,全采用缺省形式构造函数。 当基类声明有带参构造函数时,派生类也必须声明带参构造函数,并将参数传递给基类构造函数。,派生类的构造、析构函数,构造函数的调用次序 首先调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从
45、左向右)。 然后调用成员对象的构造函数,调用顺序按照它们在类中声明的顺序。 最后,执行派生类的构造函数体中的语句。,派生类的构造、析构函数,继承时的析构函数 析构函数也不被继承,派生类自行声明 声明方法与一般(无继承关系时)类的析构函数相同。 不需要显式地调用基类的析构函数,系统会自动隐式调用 析构函数的调用次序与构造函数相反。,派生类的构造、析构函数,派生类析构函数举例 #include using namespace std; class B1/基类B1声明 public:B1(int i) cout“constructing B1 “iendl;B1() cout“destructing
46、 B1 “endl; ; class B2/基类B2声明 public:B2(int j) cout“constructing B2 “jendl;B2() cout“destructing B2 “endl; ; class B3/基类B3声明 public:B3()cout“constructing B3 *“endl;B3() cout“destructing B3 “endl; ;,派生类的构造、析构函数,派生类析构函数举例 class C: public B2, public B1, public B3 public:C(int a, int b, int c, int d):B1(
47、a), memberB2(d), memberB1(c), B2(b) private:B1 memberB1;B2 memberB2;B3 memberB3; ; void main() C obj(1, 2, 3, 4); ,派生类的构造、析构函数,派生类析构函数举例 constructing B2 2 constructing B1 1 constructing B3 * constructing B1 3 constructing B2 4 constructing B3 * destructing B3 destructing B2 destructing B1 destructin
48、g B3 destructing B1 destructing B2,类成员的标识与访问,二义性问题 在继承时,基类之间、或基类与派生类之间发生成员同名时,将出现对成员访问的不确定性同名二义性。 当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,将产生另一种不确定性路径二义性。,类成员的标识与访问,同名隐藏的原理尽管基类与派生类在声明时是并列的:可逻辑语义上是嵌套的:大概念包含了小概念。自然地,在子类的地盘上,子类的标识符会屏蔽父类的同名标识符。若想启用(“捞出来”),用: :,类成员的标识与访问,解决同名二义的方法当派生类与基类有同名成员时,派生类中的成员将屏蔽基类中的同名成员。若未特别指明,则通过派生类对象使用的都是派生类中的同名成员;如要通过派生类对象访问基类中被屏蔽的同名成员,应使用基类名限定(:)。,