C++类与继承
- 格式:doc
- 大小:64.50 KB
- 文档页数:9
C++——类继承以及类初始化顺序对于类以及类继承, ⼏个主要的问题:1) 继承⽅式: public/protected/private继承.这是c++搞的, 实际上继承⽅式是⼀种允许⼦类控制的思想. ⼦类通过public继承, 可以把基类真实还原, ⽽private继承则完全把基类屏蔽掉.这种屏蔽是相对于对象层⽽⾔的, 就是说⼦类的对象完全看不到基类的⽅法, 如果继承⽅式是private的话, 即使⽅法在基类中为public的⽅法.但继承⽅式并不影响垂直⽅向的访问特性, 那就是⼦类的函数对基类的成员访问是不受继承⽅式的影响的.⽐较(java): java是简化的, 其实可认为是c++中的public继承. 实在没必要搞private/protected继承, 因为如果想控制,就直接在基类控制就好了.2) 对象初始化顺序: c++搞了个成员初始化列表, 并确明确区分初时化跟赋值的区别. c++对象的初始化顺序是:(a) 基类初始化(b) 对象成员初时化(c) 构造函数的赋值语句举例:假设 class C : public A, public B {D d;//}则初始化的顺序是A, B, D, C的构造函数.这⾥基类的初始化顺序是按照声明的顺序, 成员对象也是按照声明的顺序. 因此 c(int i, int j) : B(i), A(j) {} //这⾥成员初始化列表的顺序是不起作⽤的;析构函数的顺序则刚好是调过来, 构造/析构顺序可看作是⼀种栈的顺序;⽐较(java): java中初始化赋值是⼀回事. ⽽且对基类的构造函数调⽤必须显⽰声明, 按照你⾃⼰写的顺序.对成员对象, 也叫由你初始化.没有什么系统安排的顺序问题, 让你感觉很舒服;3) 多继承问题: c++⽀持多继承, 会导致"根"不唯⼀. ⽽java则没有该问题;此外c++没有统⼀的root object, java所有对象都存在Object类使得很多东西很⽅便. ⽐如公共的seriall, persistent等等.4) 继承中的重载: c++中, 派⽣类会继承所有基类的成员函数, 但构造函数, 析构函数除外.这意味着如果B 继承A, A(int i)是基类构造函数, 则⽆法B b(i)定义对象. 除⾮B也定义同样的构造函数.c++的理由是, 假如派⽣类定义了新成员, 则基类初始化函数⽆法初始化派⽣类的所有新增成员.⽐较(java): java中则不管, 就算有新增对象基类函数没有考虑到, ⼤不了就是null, 或者你⾃⼰有缺省值. 也是合理的.5) 继承中的同名覆盖和⼆义性: 同名覆盖的意思是说, 当派⽣类跟基类有完全⼀样的成员变量或者函数的时候, 派⽣类的会覆盖基类的.类似于同名的局部变量覆盖全局变量⼀样. 但被覆盖的基类成员还是可以访问的.如B继承A, A, B都有成员变量a,则B b, b.a为访问B的a, b.A::a 则为访问基类中的a. 这对于成员函数也成⽴.但需要注意的是, 同名函数必须要完全⼀样才能覆盖. int func(int j)跟int func(long j)其实是不⼀样的. 如果基类,派⽣类有这两个函数, 则不会同名覆盖.最重要的是, 两者也不构成重载函数. 因此假如A有函数int func(int j), B有函数int func(long j). 则B的对象b.func(int)调⽤为错误的. 因为B中的func跟它根本就不构成重载.同名覆盖导致的问题是⼆义性. 假如C->B=>A, 这⾥c继承B, B继承A. 假如A, B都有同样的成员fun, 则C的对象c.fun存在⼆义性. 它到底是指A 的还是B的fun呢?解决办法是⽤域限定符号c.A::fun来引⽤A的fun.另外⼀个导致⼆义性的是多重继承. 假设B1, B2都继承⾃B, D则继承B1, B2. 那么D有两个B⽽产⽣⼆义性.这种情况的解决办法是⽤虚基类. class B1 : virtual public B, class B2:virtual public B, D则为class D : public B1, public B2. 这样D中的成员只包含⼀份B的成员使得不会产⽣⼆义性.⽐较(java). java中是直接覆盖. 不给机会这么复杂, 还要保存基类同名的东西. 同名的就直接覆盖, 没有同名的就直接继承.虚基类的加⼊, 也影响到类的初始化顺序. 原则是每个派⽣类的成员化初始化列表都必须包含对虚基类的初始化.最终初始化的时候, 只有真正实例化对象的类的调⽤会起作⽤. 其它类的对虚基类的调⽤都是被忽略的. 这可以保证虚基类只会被初始化⼀次.c++没有显式接⼝的概念, 我觉得是c++语⾔的败点. 这也是导致c++要⽀持组件级的重⽤⾮常⿇烦. 虽然没有显式的接⼝, 但c++中的纯虚函数以及抽象类的⽀持, 事实上是等同于接⼝设施的. 当⼀个类中, 所有成员函数都是纯虚函数, 则该类其实就是接⼝.java c++接⼝类(所有成员函数都是纯虚函数)抽象类类(部分函数是虚函数)对象类对象类C++构造函数调⽤顺序1. 如果类⾥⾯有成员类,成员类的构造函数优先被调⽤;2. 创建派⽣类的对象,基类的构造函数优先被调⽤(也优先于派⽣类⾥的成员类);3. 基类构造函数如果有多个基类,则构造函数的调⽤顺序是某类在类派⽣表中出现的顺序⽽不是它们在成员初始化表中的顺序;4. 成员类对象构造函数如果有多个成员类对象,则构造函数的调⽤顺序是对象在类中被声明的顺序⽽不是它们出现在成员初始化表中的顺序;5. 派⽣类构造函数,作为⼀般规则派⽣类构造函数应该不能直接向⼀个基类数据成员赋值⽽是把值传递给适当的基类构造函数,否则两个类的实现变成紧耦合的(tightly coupled)将更加难于正确地修改或扩展基类的实现。
类之间的关系在大体上分为两种,一种是纵向的,另一种是横向的。
一、纵向的就是继承,它是OO的三个特征之一。
在UNL中称作:泛化(Generalization) 表示为:实现(Realization) 表示为:◆泛化泛化关系: 是一种继承关系, 表示一般与特殊的关系, 它指定了子类如何特化父类的所有特征和行为。
表示类与类之间的继承关系,接口与接口之间的继承关系。
一般化的关系是从子类指向父类的,与继承或实现的方法相反。
// Animal.hclass CAnimal{public:// implementvirtual HRESULT EatSomething(){// Do something}};// Tiger.h#include "Animal.h"class CTiger : public CAnimal{// Do something};◆实现实现关系: 是一种类与接口的关系, 表示类是接口所有特征和行为的实现。
// Animal.hclass CAnimal{public:// interfacevirtual HRESULT EatSomething() = 0;};// Tiger.h#include "Animal.h"class CTiger : public CAnimal{// Do something};注: 泛化和实现的区别就在于子类是否继承了父类的实现, 如有继承则关系为泛化, 反之为实现.二、横向关系,按UML关系分为4种,依赖(Dependency),表示为:--------→即虚线+箭头关联(Association),表示为:实线+箭头聚合(Aggregation),表示为:空心菱形+实线组合(Composition),表示为:实心菱形+实线它们的强弱关系是:依赖< 关联< 聚合< 组合,◆依赖依赖就是某个对象的功能依赖于另外的某个对象,而被依赖的对象只是作为一种工具在使用,而并不持有对它的引用。
c语言三种继承方式C语言中的继承方式有三种,分别是单继承、多继承和多重继承。
1. 单继承在C语言中,单继承是指一个类只能继承自一个父类。
通过单继承,子类可以继承父类的成员变量和成员函数,并且可以在子类中对这些成员进行重写或扩展。
这种继承方式可以实现代码的重用和扩展,提高了代码的可维护性和可读性。
2. 多继承多继承是指一个类可以同时继承自多个父类。
通过多继承,子类可以继承多个父类的成员变量和成员函数。
在C语言中,可以通过结构体来实现多继承的效果。
子类可以通过结构体嵌套的方式,将多个父类的成员变量和成员函数组合在一起,从而实现多继承的效果。
多继承可以在一定程度上提高代码的复用性,但也增加了代码的复杂性和理解难度。
3. 多重继承多重继承是指一个类同时继承自多个父类,并且这些父类之间存在继承关系。
通过多重继承,子类可以继承多个父类的成员变量和成员函数,并且可以通过继承链的方式,依次调用父类的成员函数。
在C语言中,可以通过结构体嵌套的方式来实现多重继承。
多重继承可以实现更复杂的代码结构,但也增加了代码的复杂性和维护难度。
继承是面向对象编程中的重要概念,通过继承可以实现代码的重用和扩展。
在C语言中,可以通过结构体嵌套的方式来模拟继承的效果。
通过继承,可以将相关的代码组织在一起,提高代码的可读性和可维护性。
在实际的程序设计中,选择何种继承方式应根据具体的需求和设计考虑。
单继承适用于简单的继承关系,多继承适用于需要同时继承多个父类的情况,多重继承适用于父类之间存在继承关系的情况。
不同的继承方式在代码结构和功能实现上有所不同,需要根据实际情况进行选择。
在使用继承时,需要注意继承关系的合理性和代码的可维护性。
继承关系应符合面向对象编程的设计原则,避免出现过于复杂的继承链和多重继承导致的代码混乱。
同时,需要注意在子类中对父类成员的访问权限控制,避免破坏封装性和安全性。
C语言中的继承方式包括单继承、多继承和多重继承。
通过继承,可以实现代码的重用和扩展,提高代码的可维护性和可读性。
C++习题3(继承和多态)C++习题3(继承和多态)一、选择题1、在C++中,类与类之间的继承关系具有( C )A)自反性 B)对称性 C)传递性 D)反对称性2、在公有继承的情况下,基类的成员(私有的除外)在派生类中的访问权限( B )A)受限制 B)保持不变 C)受保护 D)不受保护3、按解释中的要求在下列程序划线处填入的正确语句是:( C )#include class Base{public:void fun(){cout<<"Base::fun"<<=""> fun()cout<<"Derived::fun"<fun();4、在保护继承的情况下,基类的成员(私有的除外)在派生类中的访问权限( C )A)受限制 B)保持不变 C)受保护 D)不受保护5、在哪种派生方式中,派生类可以访问基类中的protected 成员(B )A)public和private B)public、protected和private C)protected和private D)仅protected6、当一个派生类仅有protected继承一个基类时,基类中的所有公有成员成为派生类的(C)A)public成员 B)private成员 C)protected成员 D)友元7、不论派生类以何种方法继承基类,都不能使用基类的(B )A)public成员 B)private成员 C)protected成员 D)public成员和protected成员8下面叙述错误的是(S )。
A )基类的protected成员在派生类中仍然是protected的 B)基类的protected成员在public派生类中仍然是protected的C)基类的protected成员在private派生类中是private的 D)基类的protected 成员不能被派生类的对象访问9、下列说法中错误的是(S )。
C++作业答案第7章继承7.1 选择题1.在c++中,类与类之间的继承关系具有( c )。
(a) 自反性 (b) 对称性 (c) 传递性 (d) 反对称性2.下列关于类的继承描述中,( a,b )是错误的。
(a) 派生类可以访问基类的所有数据成员,调用基类的所有成员函数(b) 派生类也是基类,所以基类具有派生类的全部属性和方法(c) 继承描述类的层次关系,派生类可以具有与基类相同的属性和方法(d) 一个基类可以有多个派生类,一个派生类可以有多个基类3.当一个派生类公有继承一个基类时,基类中的所有公有成员成为派生类的( a )。
(a) public 成员 (b)private成员(c) protected成员 (d)友员4.当一个派生类私有继承一个基类时,基类中的所有公有成员和保护成员成为派生类的( b )。
(a) public 成员 (b)private成员(c) protected成员 (d)友员5.当一个派生类保护继承一个基类时,基类中的所有公有成员和保护成员成为派生类的( c )。
(a) public 成员 (b)private成员(c) protected成员 (d)友员6.不论派生类以何种方式继承基类,都不能使用基类的( b )。
(a) public 成员 (b)private成员(c) protected成员 (d)public 成员和protected成员7.下面描述中,错误的是( b, c )。
(a) 在基类定义的public成员在公有继承的派生类中可见,也能在类外被访问(b) 在基类定义的public和protected成员在私有继承的派生类中可见,在类外可以被访问(c) 在基类定义的public和protected成员在保护继承的派生类中不可见(d) 在派生类中不可见的成员要变成可访问的需进行访问声明8.在c++中,不能被派生类继承的函数是( b,c )。
(a) 成员函数 (b)构造函数(c) 析构函数 (d)静态成员函数9.在创建派生类对象时,构造函数的执行顺序是( d )。
c面向对象程序课程设计一、教学目标本节课的教学目标是让同学们掌握C面向对象程序的基本概念和语法,包括类、对象、继承、多态等核心概念。
通过学习,同学们能够熟练使用C++编写简单的面向对象程序,培养编程思维和解决问题的能力。
同时,在学习过程中,培养同学们的团队合作意识和自主学习能力。
二、教学内容本节课的教学内容主要包括以下几个部分:1.面向对象程序概述:介绍面向对象程序的基本概念,如类、对象、封装、继承、多态等。
2.类和对象:讲解如何定义一个类,如何创建和使用对象,以及构造函数和析构函数的概念。
3.继承:介绍继承的概念和实现方式,以及继承的好处和注意事项。
4.多态:讲解多态的概念、实现方式以及多态的好处。
5.面向对象程序设计实例:通过实例讲解如何运用面向对象程序设计的方法解决实际问题。
三、教学方法为了更好地实现教学目标,本节课采用以下几种教学方法:1.讲授法:讲解面向对象程序的基本概念、语法和实例。
2.讨论法:同学们进行小组讨论,分享学习心得和解决问题的方法。
3.案例分析法:分析实际编程案例,让同学们更好地理解面向对象程序设计的方法。
4.实验法:安排课后的编程实践,让同学们动手编写面向对象的程序,巩固所学知识。
四、教学资源为了支持本节课的教学内容和教学方法的实施,准备以下教学资源:1.教材:《C++面向对象程序设计》等相关教材。
2.参考书:提供一些关于C++面向对象程序设计的参考书籍,供同学们课后自主学习。
3.多媒体资料:制作精美的PPT,直观地展示面向对象程序设计的相关概念和实例。
4.实验设备:提供计算机和编程环境,让同学们能够进行实际的编程练习。
五、教学评估本节课的教学评估将采用多元化的评估方式,以全面、客观、公正地评价同学们的学习成果。
评估方式包括:1.平时表现:考察同学们在课堂上的参与程度、提问回答、小组讨论等,占总评的20%。
2.作业:布置与课程内容相关的编程作业,让同学们巩固所学知识,占总评的30%。
第2章类和对象一、选择题1、()不是构造函数的特征。
A、构造函数的函数名与类名相同;B、构造函数可以重载;C、构造函数可以设置缺省参数;D、构造函数必须指定类型说明。
2、下列关于构造函数的描述中,()是正确的。
A、构造函数可以设置缺省参数;B、构造函数可以被继承;C、构造函数可以对静态数据成员进行初始化;D、构造函数可以说明为虚函数;3、()是析构函数的特征。
A、析构函数可以有一个或多个参数;B、析构函数名与类名不同;C、析构函数的定义只能在类体内;D、一个类中只能定义一个析构函数;4、定义析构函数时,应该注意()。
A、其函数名与类名完全相同;B、函数返回类型是void类型;C、无形参,也不可重载;D、函数体中必须有delete语句;5、下列静态数据成员的特性中,()是错误的。
A、说明静态数据成员时前边要加修饰符static;B、静态数据成员要在类体外进行初始化;C、静态数据成员不是所有对象所共用的;D、引用静态数据成员时,要在其名称前加<类名>和作用域运算符;6、友元的作用是()。
A、提高程序的运用效率;B、加强类的封装性;C、实现数据的隐藏性;D、增加成员函数的种类;7、关于成员函数特征的下列描述中,()是错误的。
A、成员函数一定是内联函数;B、成员函数可以重载;C、成员函数可以设置缺省参数值;D、成员函数可以是静态的;8、已知:print()函数是一个类的常成员函数,它无返回值,下列表示中,()是正确的;A、void print() const;B、const void print();C、void const print();D、void print(const);9、在()情况下适宜采用inline定义内联函数。
A、函数体含有循环语句;B、函数体含有递归语句;C、函数代码小,频繁调用;D、函数代码多,不常调用;10、假定AB为一个类,则执行“AB a(4) , b[3] , * p[2] ;”语句时,自动调用该类构造函数的次数为( )。
C++随堂测试(继承、多态、重载)一、选择题()1.在C++中,类与类之间的继承关系具有()A)自反性 B)对称性 C)传递性 D)反对称性2.在保护继承的情况下,基类的成员(私有的除外)在派生类中的访问权限()A)受限制 B)保持不变 C)受保护 D)不受保护3.在下列运算符中,不能重载的是( )A.!B. sizeofC. newD. delete4.不能用友元函数重载的是( )。
A.=B.==C.<=D.++5.在哪种派生方式中,派生类可以访问基类中的protected成员()A)public和private B)public、protected和privateC)protected和private D)仅protected6.下列函数中,不能重载运算符的函数是( )。
A.成员函数B.构造函数C.普通函数D.友元函数7.下面叙述错误的是()。
A)基类的protected成员在派生类中仍然是protected的B)基类的protected成员在public派生类中仍然是protected的C)基类的protected成员在private派生类中是private的D)基类的protected成员不能被派生类的对象访问8.如果表达式++i*k时中的”++”和”*”都是重载的友元运算符,则采用运算符函数调用格式,该表达式还可表示为()。
A.operator*(i.operator++(),k)B.operator*(operator++(i),k)C.i.operator++().operator*(k) D.k.operator*(operator++(i))9.下面叙述错误的是()。
A)派生类可以使用private派生B)对基类成员的访问必须是无二义性的C)基类成员的访问能力在派生类中维持不变D)赋值兼容规则也适用于多继承的组合10.下列虚基类的声明中,正确的是:()A)class virtual B: public A B)class B: virtual public AC)class B: public A virtual D)virtual class B: public A11.已知在一个类体中包含如下函数原型:VOLUME operator-(VOLUME)const;下列关于这个函数的叙述中,错误的是()。
C++类总结类中创建的每一个新对象都有自己的存储空间,用于存储其内部变量和类成员,但同一个类的所有对象共享同一组类方法,即每种方法只有一个副本。
Const 成员函数例const Stock land = Stock (“Kludgehorn Properties”);land.show();第二句将拒绝编译,show()的代码无法保证被调对象不被修改,所以要这样声明show() Void show()const;构造函数的参数不是类成员,而是赋给类成员的值,因此参数名不能与类成员名相同。
构造函数的使用分为显式和隐式。
显式例:Stock food =Stock (“world cabbage”,250,1.25);隐式例:Stock garment(“furry mason”,50,2.5);用new创建类对象时,C++都将使用类构造函数。
例:Stock *pstock=new Stock(“electroshock games”,18,19.0);当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数。
为类定义了构造函数后,程序员就必须为它提供默认构造函数。
构造函数不仅仅可用于初始化新对象,还可以用来对已有对象赋值。
例如stock1为已有对象。
Stock1=stock(“nifty foods”,10,50.0);析构函数的用途:做类的善后处理。
例如构造函数使用new来分配内存,则析构函数将使用delete来释放这些内存。
每个成员函数(包括构造函数和析构函数)都有一个this指针。
This指针指向调用对象。
如果方法需要引用整个调用对象,则可以使用表达式*this。
在函数的括号后面使用const限定符将this限定为const,这样将不能使用this来修改对象的值。
如const stock & topval(const stock & s)const;要创建对象数组,则这个类必须有默认构造函数。
当想创建一个由所有对象共享的常量时不可以直接用如const int len =30;的方式。
可以通过枚举enum {len=30};或使用static关键字static const int len =30;(此时len与其他静态变量存储在一起,而不是存储在类对象中。
运算符重载限制1.重载运算符必须至少有一个操作数是用户定义的类型,这将防止用户为标准型重载操作符。
2.使用操作符时不能违反操作符原来的句法规则。
不能修改操作符的优先级。
3.不能定义新的操作符。
4.不能重载下面的操作符。
“Sizeof”“.”(成员操作符)“.*”(成员指针操作符) “::”(作用域解析操作符) “?:”(条件操作符)typeid(一个RTTI操作符),const_cast(强制类型转换操作符),dynamic_cast(强制类型转换操作符),reinterpret_cast(强制类型转换操作符),static_cast(强制类型转换操作符)5.大多数操作符都可以通过成员或非成员函数进行重载,但下面的操作符只能通过成员函数进行重载:“=”“()”“[]”“->”友元友元函数:可以访问类的私有成员的非成员函数。
不可以在定义中使用关键字friend。
友元类:当两个类不属于is-a关系时可申明友元类。
friend class remote;友元声明可以位于公有,私有或保护部分,其所在的位置无关紧要。
友元类可以访问私有函数。
友元成员函数:类中的成员函数可以声明为其他类的友元函数。
Class tv;Class remote{};Class tv{Friend void remote::set_chan(tv & t,int c);}注意类的声明次序类的自动转换和强制类型转换C++语言不自动转换不兼容的类型。
例如int *p=10;是非法的,左边为指针类型右边为int 型。
C++程序员可以指示C++如何自动进行类的类型转换或强制类型转换。
接受一个参数的构造函数为将类型与该参数相同的值转换为类提供了方法。
例class stonewt{。
stonewt(double lbs);}stonewt mycat;mycat =19.6;在例中构造函数被隐式调用了将19.6转化为类类型。
有时不需要这种隐式的转化,在声明构造函数时使用explicit关键字关闭这种特性。
例如:用explicit stonewt(double lbs);声明构造函数。
则stonewt mycat;mycat =19.6;wrongmycat=stonewt(19.6);rightmycat=(stonewt)19.6;right当stonewt(double)没有用explicit声明时,可用于下面的隐式转换。
1.将stonewt对象初始化为double值时。
2.将double值赋给stonewt对象时。
3.将double值传递给接受stonewt参数的函数时。
4.返回值被声明为stonewt的函数试图返回一个double值时。
5.在上述任意一种情况下,使用可转换为double类型的内置类型时。
例如stonewt jumbo(7000);Jumbo=7300;如果想将类类型转化到别的类型可使用转换函数,转换函数使用户定义的强制类型转换。
Operator typename()例如可以在类中声明operator double();函数,将类类型转化为double 类型。
在类中的静态成员(static),无论创建了多少对象,程序都只创建一个静态类变量副本。
也就是说类的所有对象共享同一个静态成员。
不能在类声明中初始化静态成员变量。
这是因为声明描述了如何分配内存,但并不分配内存。
可以用如下语句初始化:typename classname ::variable =value;默认构造函数如果不提供任何构造函数,c++将创建默认构造函数。
默认构造函数使类类似一个常规的自动变量,也就是说,它的值在初始化时是未知的。
定义默认构造函数时可以没有任何参数,也可以有参数,但所有参数都要有默认值。
如:Stonewt (){};stonewt(int n=0){stonewt_ct =0};复制构造函数复制构造函数用于将一个对象复制到新创建的对象中。
也就是说,它用于初始化过程中。
而不是常规的赋值过程。
类的复制构造函数原型通常如下:Class_name (const class_name &);特别注意:Classname & classname::functionname(const classname & variable)该函数返回的是指向该类对象的引用。
类的继承:派生类不能直接访问积累的私有成员,而必须通过基类进行访问:例如father为基类,son 为派生类,father的私有成员为fn,ln,ht。
son的私有成员为r。
构造函数应为:Son ::son(unsigned int r,const char * fn,const char *ln,bool ht):father(fn,ln,ht);派生类构造函数的要点如下:1,基类对象首先被创建。
2,派生类构造函数应通过成员初始化列表降级类信息传递给积累构造函数。
3,派生类构造函数应该初始化派生类新增的数据成员。
类和基类之间有一些特殊的关系。
1,派生类对象可以使用基类的方法(不是私有方法)2,基类指针可以在不进行显示类型转换的情况下指向派生类对象。
3,基类引用可以在不进行显示类型转换的情况下引用派生类对象。
但是,基类指针或引用只能调用基类方法。
继承关系分为三种:公有继承,is-a关系:派生类对象也是一个基类对象,可以对基类对象执行的任何操作也可以对派生类对象执行。
多态1在派生类中重新定义基类的方法。
2使用虚方法(virtual)。
如果没有关键字virtual程序将根据引用类型或指针类型选择方法,如果使用了virtual程序将根据引用或指针指向的对象的类型来选择方法。
基类中声明虚析构函数,保证释放派生对象时按正确顺序调用析构函数。
纯虚函数virtual typename funname() = 0;类成员初始化列表的句法如果classy是一个类,而mem1、mem2、mem3都是这个类的数据成员,则类构造函数可以使用如下的句法来初始化数据成员。
classy::classy(int n,int m): mem1(n), mem(0), mem(n*m+2)注意:初始化列表包含多个项目时,这些初始化项目的初始化顺序为它们的声明顺序。
附录:RTTI是“Runtime Type Information”的缩写,意思是:运行时类型信息。
它提供了运行时确定对象类型的方法。
两个重要的RTTI 运算符的使用方法,它们是typeid 和dynamic_cast。
一些面向对象专家在传播自己的设计理念时,大多都主张在设计和开发中明智地使用虚拟成员函数,而不用RTTI 机制。
但是,在很多情况下,虚拟函数无法克服本身的局限。
每每涉及到处理异类容器和根基类层次(如MFC)时,不可避免要对对象类型进行动态判断,也就是动态类型的侦测。
如何确定对象的动态类型呢?答案是使用内建的RTTI 中的运算符:typeid 和dynamic_cast。
首先让我们来设计一个类层次,假设我们创建了某个处理文件的抽象基类。
它声明下列纯虚拟函数:open()、close()、read()和write():class File{public:virtual int open(const string & filename)=0;virtual int close(const string & filename)=0;virtual ~File()=0; // 记住添加纯虚拟析构函数(dtor)};现在从File 类派生的类要实现基类的纯虚拟函数,同时还要提供一些其他的操作。
假设派生类为DiskFile,除了实现基类的纯虚拟函数外,还要实现自己的flush()和defragment()操作:class DiskFile: public File{public:int open(const string & filename);// 自己的专有操作virtual int flush();virtual int defragment();};接着,又从DiskFile 类派生两个类,假设为TextFile 和MediaFile。
前者针对文本文件,后者针对音频和视频文件:class TextFile: public DiskFile{int sort_by_words();};class MediaFile: public DiskFile{//......};我们之所以要创建这样的类层次,是因为这样做以后可以创建多态对象,如:File *pfile; // *pfile的静态类型是Fileif(some_condition)pfile = new TextFile; // 动态类型是TextFileelsepfile = new DiskFile; // 动态类型是DiskFile利用运算符typeid 可以获取与某个对象关联的运行时类型信息。