1. 类与对象
- 结构变量占有一片连续内存空间,具有结构类型的特征
- 一个结构类型的成员可以是另一个已定义的结构类型,但不能嵌套定义
- 声明结构变量可以同时初始化,例如:
struct employee
{
char name[10];
long code;
}worker={“Wang Li”,”991083456”};
-
类型相同的结构变量可以整体赋值
-
在类外定义成员函数
void Tdata::Set(int m){…}
-
允许已定义类名出现在类的说明中(可在类中生命自身类型的指针)
-
不能嵌套定义类
-
类可以无名,用于直接声名对象
-
空类对象大小不为零,空类对象具有地址
-
共有成员是提供给外界的接口
-
类外用.和->运算符访问对象成员
-
建立动态对象
Tclass * pt = new(Tclass)
-
this指针不能显式声明,可以显示使用
-
在对象声明时初始化,即通过调用函数的对象this指针获取对象的地址
-
this指针是指向常量的常指针
void constFun()
{x++;y++:}//正确
void constFun(const Simple * const this)
void constFun()
{x++;y++;}//错误,不能修改对象的数据成员
-
构造函数的作用:为对象分配空间,对数据成员赋初值,请求其他资源
-
构造函数可以有任意类型的参数,但没有返回类型
-
当一个对象的作用域结束时,系统自动调用析构函数
-
构造函数没有返回类型,析构函数没有参数也没有返回类型
-
delete掉一个动态对象时,调用析构函数
-
构造函数的“初始式”可以调用类类型成员或基类构造函数,如:B(int x,int y):aa(x),b(y){…}
-
复制构造函数
-
用一个已有同类对象的数据对正在建立的对象进行数据初始化
-
语法形式:
类名::类名(const 类名 & 引用名, ...);
-
class A{ public: A(int); //构造函数 A(const A &,int =1); //复制构造函数 ... }; ... A a(1);//创建对象a,调用构造函数 A b(a,0);//创建对象b,调用复制构造函数(用法1) A c = b;//创建对象c,调用复制构造函数(用法2)
-
用已有对象初始化新创建对象,如
Location A(1,2); Location B(A);
-
函数的类类型实型参数初始化形参时,要调用复制构造函数。如:
-
#include <iostream.h> class Location { public: Location( int xx = 0 , int yy = 0 ) //构造函数 { X = xx ; Y = yy ; cout << "Constructor Object.\n" ; } Location( const Location & p ) //复制构造函数 { X = p.X ; Y = p.Y ; cout << "Copy_constructor called." << endl ; } ~Location() { cout << X << "," << Y << " Object destroyed." << endl ; } int GetX () { return X ; } int GetY () { return Y ; } private : int X , Y ; } ; void f ( Location p )//一个带类参数的函数 { cout << "Funtion:" << p.GetX() << "," << p.GetY() << endl ; } //A给p传值时,调用赋值构造函数构造形参p void main() { Location A ( 1, 2 ) ; //创立对象调用构造函数 f ( A ) ;//传值函数,把对象A的数据复制到形参p }
-
函数返回类类型时,通过复制构造函数建立临时对象
-
Location g(){ Location A(1,2); } void main(){ Location B; B=g();//执行默认赋值重载运算,匿名对象数据赋给对象B }
-
-
对象的数据资源是由指针指示的堆时,默认复制构造函数 仅作指针值复制
-
//一个有问题的程序 #include<iostream.h> #include<string.h> class name { public : name(char *pn) ; ~ name() ; protected : char *pname ; int size ; } ; name :: name(char *pn) { cout <<" Constructing " << pn << endl ; pname = new char[strlen(pn)+1] ; if (pname!=0) strcpy(pname,pn) ;//使Obj1.pname这个指针指向NoName size = strlen(pn) ; } name :: ~ name() { cout << " Destructing " << pname << endl ; pname[0] = '\0' ; delete []pname ;//析构错误,内存已释放 size = 0 ; } void main() { name Obj1("NoName") ; name Obj2 = Obj1 ;//两个对象共享内存导致操作异常 } //修改两个函数: name::name(const name &Obj)//用复制构造函数不会有内存冲突 { cout << " Copying " << Obj.pname << " into its own block\n" ; pname = new char[strlen(Obj.pname)+1] ; if (pname!=0) strcpy(pname, Obj.pname) ; size = Obj.size ; } name :: ~ name()//正确析构函数 { cout << " Destructing " << pname << endl ; pname[0] = '\0' ; delete []pname ; size = 0 ; }
-
静态成员
-
关键字 static 可以用于说明一个类的成员
-
静态成员提供了一个同类对象的共享机制
-
把一个类的成员说明为 static 时,这个类无论有多少个对象被 创建,这些对象共享这个 static 成员
-
静态成员局部于类,它不是对象成员
-
声明与定义静态数据成员:
-
class counter { static int num ; public : void setnum ( int i ) { num = i ; } void shownum() { cout << num << '\t' ; } } ; int counter :: num = 0 ;
-
成员函数可以直接访问静态数据成员
-
调用成员函数访问私有静态数据成员:
-
void main () { counter a , b ; a.shownum() ; b.shownum() ; a.setnum(10) ; a.shownum() ;//访问同一个静态数据成员 b.shownum() ; }
-
静态成员函数提供不依赖于类数据结构的共同操作,它没有this指针
-
在类外调用静态成员函数用 “类名 :: ”作限定词,或通过对象调用
-
class X { int DatMem ; public : static void StaFun ( int i , X *ptr ) ; } ; void X :: StaFun ( int i , X * ptr ) { DatMen = i;//错误,不知DatMen引自哪一个对象 ptr->DatMen=i;//正确 } void g() { X obj ; X :: StaFun ( 1, & obj ) ;//正确 obj.SatFun( 1, & obj ) ;//正确,都表示静态成员函数的地址 }
-
-
友员函数
-
class A { private: int i ; friend void FriendFun(A * , int) ; //语句位置与访问描述无关,也可以放在类外面,友员函数不属于类 public: void MemberFun(int) ; } ; … void FriendFun( A * ptr , int x ) //友员函数通过对象参数访问私有数据成员 { ptr -> i = x ; } ; void A:: MemberFun( int x ) { i = x ; } ;//成员函数通过this指针在对象上操作
-
友员类
-
若F类是A类的友员类,则F类的所有成员函数都是A类的友员函数
-
友员类通常设计为一种对数据操作或类之间传递消息的辅助类
-
#include<iostream.h> class A { friend class B ;//类B是类A的友员 public : void Display() { cout << x << endl ; } ; private : int x ; } ; class B //B类没有数据成员和,仅提供对A类的操作 { public : void Set ( int i ) { Aobject . x = i ; } //通过类成员访问A类的私有数据成员 void Display () { Aobject . Display () ; } //通过类成员访问A类的成员函数 private : A Aobject ;//类B的A类数据成员 } ; void main() { B Bobject ; Bobject . Set ( 100 ) ; Bobject . Display () ; }
-
2. 重载运算符
-
不能重载的运算符:
. :: .* ?: sizeof
-
运算符函数是一种特殊的成员函数或友员函数
-
成员函数的语法形式为:
-
类型 类名 :: operator op ( 参数表 ) // 类型:函数返回类型;类名:重载该运算符的类;op:被重载的运算符;operator:关键字 { // 相对于该类定义的操作 }
-
一个运算符被重载后,原有意义没有失去,只是定义了相对一特定类的一个新运算符
-
#include<iostream.h> #include<stdlib.h> class Calculator { public: Calculator() { value = 0 ; } ; void operator ++ () ; void operator -- () ; unsigned int operator() () ; private: unsigned int value; }; void Calculator::operator ++ () { if ( value < 65535 ) value ++ ; else { cout << "\nData overflow !" << endl ; abort() ; } } void Calculator::operator --() { if ( value > 0 ) value -- ; else { cout << "\n Data overflow !" << endl ; abort() ; } } unsigned int Calculator::operator() () { return value ; } void main() { Calculator Counter ; int i ; for( i = 0 ; i < 5 ; i ++ ) { ++ Counter;//使用重载版本 cout << "\n Counter = " << Counter() ; } for( i = 0 ; i <= 5 ; i ++ ) { -- Counter;//使用重载版本 cout << "\n Counter = " << Counter() ;//使用重载版本 } }
-
用成员或友员函数重载运算符
- 一元运算符:
Object op 或 op Object
- 重载为成员函数,解释为:
Object . operator op ()
操作数由对象Object通过this指针隐含传递 - 重载为友员函数,解释为:
operator op (Object)
操作数由参数表的参数Object提供
- 重载为成员函数,解释为:
- 二元运算符:
ObjectL op ObjectR
- 重载为成员函数,解释为:
ObjectL . operator op ( ObjectR )
左操作数由ObjectL
通过this指针传递,右操作数由参数ObjectR
传递 - 重载为友员函数,解释为:
operator op ( ObjectL, ObjectR )
左右操作数都由参数传递
- 重载为成员函数,解释为:
- 一元运算符:
-
当一元运算符的操作数,或者二元运算符的左操作数是类的对象时,定义重载算符函数为成员函数
-
#include<iostream.h> class TriCoor { public: TriCoor( int mx = 0, int my = 0, int mz = 0 ) { x = mx ; y = my ; z = mz ; } TriCoor operator + ( TriCoor t ) { TriCoor temp ; temp.x = x+t.x ; temp.y = y+t.y ; temp.z = z+t.z ; return temp ; } /* 1、a+b -> a.operator+(b) 2、a+b+c -> Object1 + c -> Object1.operator(c) 3、c=a+b+c; c=Object2; */ TriCoor operator = ( TriCoor t ) { x = t.x ; y = t.y ; z = t.z ; return * this ; }//返回调用函数的对象 /* b=a -> b.operator=(a) c=b -> c.operator=(b) */ TriCoor operator ++ () //没有参数 { x ++ ; y ++ ; z ++ ; return *this ; } void show() { cout << x << " , " << y << " , " << z << "\n"; } void assign( int mx, int my, int mz ) { x = mx; y = my; z = mz; } private: int x, y, z ; // 3_d coordinates } ; void main() { TriCoor a( 1, 2, 3 ), b, c ; a.show(); b.show(); c.show(); for( int i = 0; i < 5; i ++ ) ++ b; b.show() ; c.assign( 3, 3, 3 ) ; c = a + b + c ; c.show() ; c = b = a ; c.show() ; }
-
友员函数重载运算符常用于运算符的左右操作数类型不同的情况
-
class Complex { int Real ; int Imag ; public : Complex ( int a ) { Real = a ; Imag = 0 ; } Complex ( int a , int b ) { Real = a ; Imag = b ; } Complex operator + ( Complex ) ; …... } ; int f ( ) { Complex z ( 2 , 3 ) , k ( 3 , 4 ) ; z = z + 27 ; //正确,z.operator(27),调用构造函数,将27转换为Complex类常量 z = 27 + z ; //错误,27不是Complex对象,不能调用函数。成员函数重载的“+”运算符不支持交换律 …... }
-
在第一个参数需要隐式转换的情形下,使用友员函数重载运算符是正确的选择
-
友员函数没有 this 指针,所需操作数都必须在参数表显式声明,很容易实现类型的隐式转换
-
C++中不能用友员函数重载的运算符有
= () [] ->
-
#include<iostream.h> class Complex { public: Complex( double r =0, double i =0 ) { Real = r ; Image = i ; } Complex(int a) { Real = a ; Image = 0 ; } void print() const ; friend Complex operator+ ( const Complex & c1, const Complex & c2 ) ;//友员函数重载"+" friend Complex operator- ( const Complex & c1, const Complex & c2 ) ; friend Complex operator- ( const Complex & c ) ;//单目“-”运算 private: double Real, Image ; }; Complex operator + ( const Complex & c1, const Complex & c2 ) { double r = c1.Real + c2.Real ; double i = c1.Image+c2.Image ; return Complex ( r, i ) ;//构造返回对象 } Complex operator - ( const Complex & c1, const Complex & c2 ) { double r = c1.Real - c2.Real ; double i = c1.Image - c2.Image ; return Complex ( r, i ) ; } Complex operator- ( const Complex & c ) { return Complex ( -c.Real, - c.Image ) ; } void Complex :: print() const { cout << '(' << Real << " , " << Image << ')' << endl ; }
-
用友员函数重载像“++”这样的运算符时,有时会碰到问题。
-
例如,类 TriCoor 用成员函数重载“++”的版本是: TriCoor TriCoor :: operator ++ () { x ++ ; y ++ ; z ++ ; return *this ; } // ok , 修改了this指针所指对象 用成员函数重载一元运算符时,所需要的唯一变元通过 this 指针传递, 对 this 所指对象数据的任何改变都会影响到激活运算符函数的对象。 1. 若定义友员函数 friend operator ++( ) 版本: TriCoor operator ++ (TriCoor opl ) { opl . x ++ ; opl . y ++ ; opl . z ++ ; return opl ; } //问题:函数使用传值参数,对形参opl 的所有修改都无法传到函数体外,不会影响被调用的对象 2. 用指向激活对象的指针定义友员函数: TriCoor operator ++ ( TriCoor * opl ) { opl -> x ++ ; opl -> y ++ ; opl -> z ++ ; return *opl ; } /*问题 C++ 不知道如何激活该函数,下述代码无法编译: TriCoor ob ( 1 , 2 , 3 ) ; &ob ++ ; // error,二义性,对ob的地址进行递加?将对象ob递加? */ 3. 使用引用参数: TriCoor operator ++ ( TriCoor & opl ) { opl . x ++; opl . y ++; opl . z ++; return opl ; } 下述代码是正确的: TriCoor ob ( 1 , 2 , 3 ) ; ob ++; // ok,传名
-
如果一个运算符的操作要修改类的对象的状态,要重载为友员函数时,应该使用引用参数。
-
若一运算符的操作需要修改类对象状态时,应该用成员函数重载; 需要左值操作数的运算符(如 ++,–),若重载为友员函数时要用引用参数
-
如果运算符的操作数(尤其是第一个操作数)希望有隐式转换,则必须用友员函数重载
-
几个典型运算符重载
- 重载++与–
-
重载赋值运算符
-
赋值运算符重载用于对象数据的复制
-
operator= 必须重载为成员函数
-
重载函数原型为:
类型 & 类名 :: operator= ( const 类名 & ) ;
-
#include<iostream.h> #include<string.h> class Name { public : Name ( char *pN ) ; Name( const Name & ) ; //复制构造函数 Name& operator=( const Name& ) ; // 重载赋值运算符 ~ Name() ; protected : char *pName ; int size ; } ; void main() { Name Obj1( "ZhangSan" ) ; Name Obj2 = Obj1 ; // 初始化对象时,调用复制构造函数 Name Obj3( "NoName" ) ; //构造函数 Obj3 = Obj2 = Obj1 ; // 修改对象时,调用重载赋值运算符函数 //析构赋值操作后的对象 }
-
-
重载运算符[]和()
-
运算符 [] 和 () 是二元运算符
-
[] 和 () 只能用成员函数重载,不能用友元函数重载
-
重载下标运算符[]
-
[] 运算符用于访问数据对象的元素
-
重载格式
类型 类 :: operator[] ( 类型 ) ;
-
设 x 是类 X 的一个对象,则表达式 x [ y ] 可被解释为 x . operator [ ] ( y ) //显式声明一个参数
-
#include<iostream.h> class vector { public : vector ( int n ) { v = new int [ n ] ; size = n ; } ~ vector ( ) { delete [ ] v ; size = 0 ; } int & operator [ ] ( int i ) { return v [ i ] ; } //返回元素的引用 this—>v[i] private : int * v ; int size ; }; void main ( ) { vector a ( 5 ) ; a [ 2 ] = 12 ; //返回引用的函数调用作左值 cout << a [ 2 ] << endl ; }
-
-
重载函数调用符()
-
() 运算符用于函数调用
-
重载格式
类型 类 :: operator() ( 表达式表 ) ;
-
例 设 x 是类 X 的一个对象,则表达式 x ( arg1, arg2, … ) 可被解释为 x . operator () (arg1, arg2, … )
-
//例6-8 用重载()运算符实现数学函数的抽象 #include <iostream.h> class F { public : double operator ( ) ( double x , double y ) ; } ; double F :: operator ( ) ( double x , double y ) { return x * x + y * y ; } void main ( ) { F f ; cout << f ( 5.2 , 2.5 ) << endl ;//f.operator(5.2,2.5) }
-
-
-
重载流插入和流提取运算符
- istream 和 ostream 是 C++ 的预定义流类
- cin 是 istream 的对象,cout 是 ostream 的对象
- 运算符 << 由ostream 重载为插入操作,用于输出基本类型数据
- 运算符 >> 由 istream 重载为提取操作,用于输入基本类型数据
- 用友员函数重载 << 和 >> ,输出和输入用户自定义的数据类型
-
//为vector类重载流插入运算符和提取运算符 #include<iostream.h> #include<stdlib.h> class vector { public : vector( int size =1 ) ; ~vector() ; int & operator[] ( int i ) ; friend ostream & operator << ( ostream & output , vector & ) ;//标准流类 output:cout的别名 ostream &:返回流类引用以符合原语义 friend istream & operator >> ( istream & input, vector & ) ;//input:cin的别名 private : int * v ; int len ; }; void main() { int k ; cout << "Input the length of vector A :\n" ; cin >> k ;//使用预定义版本 vector A( k ) ; cout << "Input the elements of vector A :\n" ; cin >> A ;//使用重载版本 operator>>(cin, A) cout << "Output the elements of vector A :\n" ; cout << A ;//使用重载版本 operator<<(cout,A) }
-
类类型转换
-
数据类型转换在程序编译时或在程序运行实现
- 基本类型 ←→ 基本类
- 型基本类型 ←→ 类类型
- 类类型 ←→ 类类型
-
类对象的类型转换可由两种方式说明:构造函数 转换函数 称为用户定义的类型转换或类类型转换,有隐式调用和显式调用方式
-
当类 ClassX 具有以下形式的构造函数
ClassX :: ClassX ( arg ,arg1 = E1 ,…, argn = En ) ;
说明了一种从参数 arg 的类型到该类类型的转换 -
class X { // …… public : X ( int ) ; X ( const char * , int = 0 ) ; }; void f ( X arg ) ; : X a = X( 1 ) ; // a = 1,调用构造函数 X ( int ) ,把 1 转换为类类型 X 后赋给对象 a,也称 X ( 1 ) 为 X 类的类型常量 X b = "Jessie" ; // b = X ( "Jessie" , 0 ),调用构造函数X ( const char * , int = 0 ) ,把字符串转换为类类型 X 后赋给对象 b a = 2 ; // a = X ( 2 ),隐式调用构造函数 X ( int ) ,把 2 转换为类类型 X 后赋给对象 f ( 3 ) ; // f ( X ( 3 ) ),隐式调用构造函数 X ( int ) ,对实参作类类型转换,然后做参数结合 f ( 10 , 20 ) ; // error,当找不到匹配的构造函数时。转换失败
-
-
类型转换函数
-
带参数的构造函数不能把一个类类型转换成基本类型
-
类类型转换函数是一种特殊的成员函数,提供类对象之间 显式类型转换的机制
-
语法形式: X :: operator T ( ) { …… return T 类型的对象 } 功能:将类型 X 的对象转换为类型 T 的对象 T 可以是预定义类型,也可以是用户定义类型 函数没有参数,没有返回类型,但必须有一条 return 语句,返回 T 类型的对象 该函数只能为成员函数,不能为友员 class X { …… public : operator int ( ) ; …… } ; void f ( X a )//a 是一个类对象,它们都用类型转换函数作类型转换,X :: operator int () { int i = int ( a ) ; i = ( int ) a ; i = a ; }
-
类型转换函数有两种使用方式:
- 隐式使用
i = a ;
- 显式使用
i = a . operator int ( ) // int ( a ) ( int ) a
- 隐式使用
-
使用不同函数作类型转换函数:
int i = a ;
// 用类型转换函数进行转换X i = a ;
// 用构造函数进行转换
-
#include<iostream.h> class Rational { public : Rational (long n , long d=1) ; // 构造函数,把长整型转换成Rational类型 friend Rational operator+ ( const Rational & ,const Rational & ) ; friend ostream & operator<< ( ostream & , const Rational & ) ; operator double() ; // 类型转换函数 private : long Numerator , Denominator ; } ; void main ( ) { Rational a( 2, 4 ), b = (1, 2), c( 0 ) ; c = a + b; // 使用友员重载 “+” 和 缺省重载 “=” cout << a << " + "<< b << " = "<< c << endl; // 以分数形式显示 // 以实数形式显示,调用类型转换函数: cout << double( a ) << " + " << double( b ) << " = " << double( c ) << endl ; c = b + Rational( 1 ) ; // 调用构造函数作类型转换 cout << b << " + " << 1 << " = " << c << endl ; cout << double( b ) << " + " << 1 << " = " << double( c ) << endl ; //类型转换函数做类型转换 }
-
3. 继承
-
类继承关系的语法形式
-
class 派生类名 : 基类名表 { 数据成员和成员函数声明 }; 基类名表 构成 访问控制 基类名1, 访问控制 基类名2 ,… , 访问控制 基类名n 访问控制 表示派生类对基类的继承方式,使用关键字: public 公有继承 private 私有继承 protected 保护继承
-
访问控制
-
公有继承 基类的公有成员 -> 派生类的公有成员
基类的保护成员 -> 派生类的保护成员
-
私有继承 基类的公有成员和保护成员 -> 派生类的私有成员
-
保护继承 基类的公有成员和保护成员 -> 派生类的保护成员
-
不论种方式继承基类,派生类都不能直接使用基类的私有成员
-
-
公有继承
-
#include<iostream.h> class A { public : void get_XY() { cout << "Enter two numbers of x, y : " ; cin >> x >> y ; } void put_XY() { cout << "x = "<< x << ", y = " << y << '\n' ; } protected: int x, y ; //保护数据成员在类层次中可见 }; class B : public A { public : int get_S() { return s ; }; void make_S() { s = x * y ; }; // 使用基类数据成员x,y protected: int s; }; class C : public B { public : void get_H() { cout << "Enter a number of h : " ; cin >> h ; } int get_V() { return v ; } void make_V() { make_S(); v = get_S() * h ; } // 使用基类成员函数 protected: int h, v; };
-
void main() { A objA ; B objB ; C objC ; cout << "It is object_A :\n" ; objA.get_XY() ; objA.put_XY() ;//对 objA 的数据成员操作 cout << "It is object_B :\n" ; objB.get_XY() ;//调用基类A成员函数对 objB 的数据成员操作 objB.make_S() ;//调用派生类B成员函数对 objB 的数据成员操作 cout << "S = " << objB.get_S() << endl ; cout << "It is object_C :\n" ; objC.get_XY() ;//调用基类A成员函数对 objC 的数据成员操作 objC.get_H();//调用派生类C成员函数对 objC 的数据成员操作 objC.make_V() ; cout << "V = " << objC.get_V() << endl ; }
-
私有继承
-
#include<iostream.h> class A { public : void get_XY() { cout << "Enter two numbers of x and y : " ; cin >> x >> y ; } void put_XY() { cout << "x = "<< x << ", y = " << y << '\n' ; } protected: int x, y ; }; class B : private A { public : int get_S() { return s ; } void make_S() { get_XY(); //调用基类成员函数 s = x * y ; } //访问B的私有数据成员 private: int s ; }; void main() { B objB ; cout << "It is object_B :\n" ; objB.make_S() ; cout << "S = " << objB.get_S() << endl ; }
-
基类的私有数据成员,不能在派生类中直接访问,但派生类对象建立私有数据空间。
-
#include<iostream.h> class A { public: A(){ x=1; } int out() {return x ; } void addX() { x++; } private: int x ; } ; class B : public A { public: B(){ y=1; } int out() {return y ; } void addY() { y++; } private: int y ; } ; void main() { A a ; cout << "a.x=" << a.out() << endl ; B b ; /*创建派生类对象 调用基类 -> 派生类构造函数 b.x = 1 b.y = 1 */ b.addX() ; // 调用基类公有成员函数 b.x ++ b.addY() ; //调用派生类公有成员函数 b.y ++ cout << "b.x=" << b.A::out() << endl ; //调用基类版本同名函数,返回 b.x 的值 cout << "b.y=" << b.out() << endl ; //调用派生类版本同名函数,返回 b.y 的值 }
-
重名成员
- 派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽了基类的同名成员
- 在派生类中使用基类的同名成员,显式地使用类名限定符:
类名 :: 成员
-
重名数据成员
-
class base { public : int a , b ; } ; class derived : public base { public : int b , c ; } ; void f () { derived d ; d . a = 1 ; //访问从base 类继承的数据成员a d . base :: b = 2 ; //访问从base 类继承的 b d . b = 3 ; //访问derived 类定义的数据成员b d . c = 4 ; //访问derived 类定义的数据成员c };
-
基类成员的作用域延伸到所有派生类
-
派生类的重名成员屏蔽基类的同名成员
-
-
重名成员函数
-
#include<iostream.h> class A { public: int a1, a2 ; A( int i1=0, int i2=0 ) { a1 = i1; a2 = i2; } void print() { cout << "a1=" << a1 << '\t' << "a2=" << a2 << endl ; } }; class B : public A { public: int b1, b2 ; B( int j1=1, int j2=1 ) { b1 = j1; b2 = j2; } void print() //定义同名函数 { cout << "b1=" << b1 << '\t' << "b2=" << b2 << endl ; } void printAB() { A::print() ; //派生类对象调用基类版本同名成员函数 print() ; //派生类屏蔽基类同名成员函数,调用自身的成员函数 } }; void main() { B b ; b.A::print(); b.printAB(); }//派生类对象调用从基类继承的同名成员函数,基类this指针指向派生类对象
-
通过继承,类B具有两个同名成员函数 void A::print(); // void print( A * this ); void B::print(); // void print( B * this ); 派生类也是基类,基类指针可以指向派生类对象 派生类中定义与基类同名的成员函数,称为重载成员函数
-
-
派生类中的静态成员
-
基类定义的静态成员,将被所有派生类共享
-
根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质
-
派生类中访问静态成员,显式说明:
类名 :: 成员
或通过对象访问:对象名.成员
-
#include<iostream.h> class B { public: static void Add() { i++ ; } //静态成员函数 static int i; //静态数据成员 void out() { cout<<"static i="<<i<<endl; } }; int B::i=0; //静态数据成员 class D : private B { public: void f(); { i=5; //i是类D的私有静态数据成员,类中可见 Add(); //Add()是类D的私有静态成员函数,类中可调用 B::i++; //访问B类的静态成员 B::Add(); //访问B类的静态成员 } }; void main() { B x; D y; x.Add(); x.out(); y.f(); cout<<"static i="<<B::i<<endl; //访问B类的静态数据成员 cout<<"static i="<<x.i<<endl; //通过B类对象访问静态数据成员 cout<<"static i="<<y.i<<endl; //错误,i是类D的私有静态数据成员,不能再类外访问 }
-
-
基类的初始化
- 在创建派生类对象时,用指定参数,调用基类的构造函数,来初始化派生类继承基类的数据
- 派生类构造函数声明为 :
派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 ) … 对象成员n ( 变元表 ) ;
-
构造函数执行顺序:基类 -> 对象成员 -> 派生类
-
#include < iostream.h > class Base { public : Base ( ) { cout << "Base created.\n" ; } } ; class D_class : public Base { public : D_class ( ) { cout << "D_class created.\n" ; } } ; void main ( ) { D_class d1 ; } //先调用基类构造函数,再调用派生类构造函数
-
带参数构造函数调用顺序测试
#include <iostream.h> class parent_class { int data1 , data2 ; public : parent_class ( int p1 , int p2 ) { data1 = p1; data2 = p2; } //基类有一个参数化的构造函数 int inc1 () { return ++ data1; } //成员函数数据成员自增 int inc2 () { return ++ data2 ; } //成员函数数据成员自增 void display () {cout << "data1=" << data1 << " , data2=" << data2 << endl ; } }; class derived_class : private parent_class { int data3 ; parent_class data4 ; //类成员 public: derived_class (int p1,int p2,int p3,int p4,int p5 ):parent_class (p1,p2),data4(p3,p4) { data3 = p5 ; } //对自身数据成员赋值 //派生类构造函数有5个参数,调用了基类构造函数 //data4(p3,p4)是构造对象成员 int inc1 ( ) { return parent_class :: inc1 ( ) ; }//调用基类版本的成员函数 int inc3 ( ) { return ++ data3 ; } //派生类数据成员自增 void display ( ) { parent_class :: display ( ) ; //调用基类成员函数,输出从基类继承的成员函数 data4.display ( ) ; //调用数据成员的成员函数,输出对象成员的数据 cout << "data3=" << data3 << endl ; //输出自身数据成员 } } ; void main ( ) { derived_class d1(17,18,1,2,-5);d1.inc1( );d1.display( );}
-
带参数构造函数调用顺序测试
#include <iostream.h> class parent_class { int data1 , data2 ; public : parent_class ( int p1 , int p2 ) { data1 = p1; data2 = p2; } int inc1 () { return ++ data1; } int inc2 () { return ++ data2 ; } void display () {cout << "data1=" << data1 << " , data2=" << data2 << endl ; } }; class derived_class : private parent_class { int data3 ; parent_class data4 ; public: derived_class (int p1,int p2,int p3,int p4,int p5):parent_class (p1,p2),data4(p3,p4) { data3 = p5 ; } int inc1 ( ) { return parent_class :: inc1 ( ) ; } int inc3 ( ) { return ++ data3 ; } void main (){ derived_class d1 (17,18,1,2,-5) ; //构造派生类对象 d1 . inc1 ( ) ; //datal自增 d1 . display ( ) ; }
-
继承的应用实例
-
考察一个点 -> 圆 -> 圆柱体的层次结构
class Point { friend ostream &operator<< (ostream &, const Point &); public: Point( int = 0, int = 0 ) ; // 带默认参数的构造函数 void setPoint( int, int ) ; // 对点坐标数据赋值 int getX() const { return x ; } int getY() const { return y ; } protected: int x, y; // Point类的数据成员 }; class Circle : public Point { friend ostream &operator<< (ostream &, const Circle &); //友员函数 public: Circle(double r=0.0, int x=0, int y=0); // 构造函数 void setRadius(double); /*置半径*/ double getRadius() const; /*返回半径*/ double area() const; // 返回面积 protected: double radius; // 数据成员,半径 }; class Cylinder:public Circle { friend ostream &operator<<(ostream &, const Cylinder &);//友员函数 public: Cylinder(double h=0.0, double r=0.0, int x=0, int y=0);//构造函数 void setHeight(double); /* 置高度值*/ double getHeight() const; /* 返回高度值*/ double area() const; /* 返回面积*/ double volume() const; /* 返回体积*/ protected: double height; // 数据成员,高度 };
-
-
多继承
-
一个类有多个直接基类的继承关系称为多继承
-
多继承声明语法:
class 派生类名:访问控制 基类名1, 访问控制 基类名2, … ,访问控制 基类名n { 数据成员和成员函数声明 };
-
执行顺序与单继承构造函数情况类似。多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序。
-
-
虚基类
-
如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性。
-
class B { public : int b ;} ; class B1 : public B { private : int b1 ; } ; class B2 : public B { private : int b2 ; } ; class C : public B1 , public B2 { public : int f ( ) ; private : int d ; } ; 有: C c ; c . B ; // error c . B :: b ; // error,从哪里继承的? c . B1 :: b ; // ok,从B1继承的 c . B2 :: b ; // ok ,从B2继承的 #include<iostream.h> void main () { C c ; c . B1 :: b = 5 ; c . B2 :: b = 10 ; cout << "path B1==> " << c . B1 :: b << endl ; cout << "path B2==> " << c . B2 :: b << endl ; } //建立 C 类的对象时,B 的构造函数将被调用两次:一次由B1调用,另一次由 B2 调用,以初始化 C 类的对象中所包含的两个 B 类的子对象
-
-
虚基类
-
如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性。
-
如果在多条继承路径上有一个公共的基类,那么在继承路径的某处汇合点,这个公共基类就会在派生类的对象中产生多个基类子对象。
-
要使这个公共基类在派生类中只产生一个子对象,必须对这个基类声明为虚继承,使这个基类成为虚基类。
-
虚继承声明使用关键字
virtual
-
class B { public : int b ;} ; class B1 : virtual public B { private : int b1 ; } ; class B2 : virtual public B { private : int b2 ; } ; class C : public B1 , public B2 { private : float d ; } ; 有: C cc ; cc . b // ok 由于类 C 的对象中只有一个 B 类子对象,名字 b 被约束到该子对象上, 所以,当以不同路径使用名字 b 访问 B 类的子对象时,所访问的都是 那个唯一的基类子对象。即 cc . B1 :: b 和 cc . B2 :: b 引用是同一个基类 B 的子对象
-
虚继承的测试
#include < iostream.h > class A { public : A ( ) { cout << "class A" << endl ; } } ; class B : public A { public : B ( ) {cout << "class B" << endl ; } } ; class C : public A { public : C ( ) {cout << "class C" << endl ; } } ; class D : public B , public C { public : D ( ) {cout << "class D" << endl ; } } ; void main ( ) { D dd ; } 输出: class A class B class A //两次调用A的构造函数 class C class D
-
#include < iostream.h > class A { public : A ( ) { cout << "class A" << endl ; } } ; class B : virtual public A { public : B ( ) {cout << "class B" << endl ; } } ; class C : virtual public A { public : C ( ) {cout << "class C" << endl ; } } ; class D : public B , public C { public : D ( ) {cout << "class D" << endl ; } } ; void main ( ) { D dd ; } 输出: class A //一次调用A的构造函数 class B class C class D
-
4. 虚函数与多态性
-
静态联编
-
联编是指一个程序模块、代码之间互相关联的过程。
-
静态联编,是程序的匹配、连接在编译阶段实现,也称为早期匹配。重载函数使用静态联编。
-
动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编。switch 语句和 if 语句是动态联编的例子。
-
普通成员函数重载可表达为两种形式:
-
在一个类说明中重载
-
Show ( int , char ) ; Show ( char * , float ) ;
-
基类的成员函数在派生类重载。有 3 种编译区分方法:
-
根据参数的特征加以区分
-
Show ( int , char ); 与 Show ( char * , float ); 不是同一函数,编译能够区分
-
使用“ :: ”加以区分
-
A :: Show ( ); 有别于 B :: Show ( );
-
根据类对象加以区分
-
Aobj . Show ( ) 调用 A :: Show ( ) Bobj . Show ( ) 调用 B :: Show ( )
-
-
-
-
类指针的关系:基类指针和派生类指针与基类对象和派生类对象4种可能匹配
-
基类指针引用派生类对象
-
通过基类指针只能访问从基类继承的成员
-
例如: class A class B:public A A * p ; // 指向类型 A 的对象的指针 A A_obj ; // 类型 A 的对象 B B_obj ; // 类型 B 的对象 p = & A_obj ; // p 指向类型 A 的对象 p = & B_obj ; // p 指向类型 B 的对象,它是 A 的派生类
-
但不能用 p访问 B 类自定义的元素 (除非用了显式类型转换)
-
#include<iostream.h> #include<string.h> class A_class { char name[20] ; public : void put_name( char * s ) { strcpy( name, s ) ; } void show_name() { cout << name << "\n" ; } }; class B_class : public A_class { char phone_num[ 20 ] ; public : void put_phone( char * num ) { strcpy ( phone_num , num ) ; } void show_phone() { cout << phone_num << "\n" ; } }; void main() { A_class * A_p ; //基类指针 A_class A_obj ; B_class B_obj ; A_p = & A_obj ; //基类指针指向基类对象 A_p -> put_name( "Wang xiao hua" ) ; //基类指针调用基类成员函数 A_p -> show_name() ; A_p = & B_obj ;//基类指针指向派生类对象 A_p -> put_name( "Chen ming" ) ; //调用从基类继承的成员函数 A_p -> show_name() ; B_obj.put_phone ("5555_12345678");//用派生类对象调用派生类的成员函数 ((B_class*)A_p)->show_phone();//对基类指针强项类型转换调用派生类的成员函数 }
-
-
派生类指针引用基类对象
-
派生类指针只有经过强制类型转换之后,才能引用基类对象
-
#include<iostream.h> #include<string.h> class Date { public: Date( int y, int m, int d ) { SetDate( y, m, d ); } void SetDate( int y, int m, int d ) { year = y ; month = m ; day = d ; } void Print() {cout << year << '/' << month << '/' << day << "; " ;} protected : int year , month , day ; } ; class DateTime : public Date { public : DateTime(int y,int m,int d,int h,int mi,int s ):Date( y, m, d ) { SetTime( h, mi, s ); } void SetTime( int h, int mi, int s ) { hours = h; minutes = mi; seconds = s; } void Print() { ((Date*)this) -> Print(); //对this指针作类型转换调用基类成员函数 //与 Data::Print();等价吗? cout<<hours<<':'<<minutes<< ':' <<seconds<<'\n'; } private: int hours , minutes , seconds ; }; void main() { DateTime dt( 2003, 1, 1, 12, 30, 0 ) ; dt.Print() ; }
-
-
-
虚函数和动态联编
-
冠以关键字 virtual 的成员函数称为虚函数
-
实现运行时多态的关键首先是要说明虚函数,另外,必须用基类指针调用派生类的不同实现版本
-
基类指针虽然获取派生类对象地址,却只能访问派生类从基类继承的成员
-
#include<iostream.h> class Base { public : Base(char xx) { x = xx; } void who() { cout << "Base class: " << x << "\n" ; } protected: char x; } ; class First_d : public Base { public : First_d(char xx, char yy):Base(xx) { y = yy; } void who() {cout<<"First derived class:"<<x<<", "<<y<<"\n";} protected: char y; } ; class Second_d : public First_d { public : Second_d(char xx,char yy,char zz):First_d( xx, yy ) { z = zz; } void who() {cout<<"Second derived class: "<<x<<", "<<y<<", "<<z<<"\n";} protected: char z; } ; void main(){ Base B_obj('A'); First_d F_obj('T','O'); Second_d S_obj( 'E', 'N', 'D' ) ; Base * p ;//定义基类指针 p = & B_obj ; p -> who() ; p = &F_obj ; p -> who() ; p = &S_obj ; p -> who() ; F_obj.who() ; //通过对象调用成员函数 ( ( Second_d * ) p ) -> who() ;//基类指针做类型转换 } 输出: Base class A Base class T Base class E First derived class:T,O Second derived class:E,N,D
-
修改程序定义虚函数
#include<iostream.h> class Base { public : Base(char xx) { x = xx; } virtual void who() {cout<<"Base class: "<<x<<"\n";} //基类定义虚函数 protected: char x; } ; class First_d : public Base { public : First_d(char xx, char yy):Base(xx) { y = yy; } void who() {cout<<"First derived class:"<<x<<", "<<y<< "\n";} //派生类的重定义版本默认为虚函数 protected: char y; } ; class Second_d : public First_d { public : Second_d(char xx,char yy,char zz):First_d(xx,yy) {z = zz;} void who() {cout<<"Second derived class:"<<x<<","<<y<<",“<<z<<"\n";} //派生类的重定义版本默认为虚函数 protected: char z; } ; void main(){ Base B_obj('A'); First_d F_obj('T','O'); Second_d S_obj('E','N','D') ; Base *p; p = & B_obj ; p -> who() ; p = &F_obj ; p -> who() ; p = &S_obj ; p -> who() ; } 输出: Base class A First derived class:T,O Second derived class:E,N,D
-
由于who()的虚特性,随着p指向不同对象,this指针作类型转换,执行不同实现版本。
-
-
虚函数和基类指针
- 一个虚函数,在派生类层界面相同的重载函数都保持虚特性
- 虚函数必须是类的成员函数
- 不能将友员说明为虚函数,但虚函数可以是另一个类的友员
- 析构函数可以是虚函数,但构造函数不能是虚函数
-
虚函数的重载特性
-
在派生类中重载基类的虚函数要求函数名、返回类型、参数个数、参数类型和顺序完全相同
-
如果仅仅返回类型不同,C++认为是错误重载
-
如果函数原型不同,仅函数名相同,丢失虚特性
-
class base { public : virtual void vf1 ( ) ; virtual void vf2 ( ) ; virtual void vf3 ( ) ; void f ( ) ; } ; class derived : public base { public : void vf1 ( ) ; // 虚函数 void vf2 ( int ) ; // 重载,参数不同,虚特性丢失 char vf3 ( ) ; // error,仅返回类型不同 void f ( ) ; // 非虚函数重载 } ; void g ( ) { derived d ; base * bp = & d ; // 基类指针指向派生类对象 bp -> vf1 ( ) ; // 调用 deriver :: vf1 ( ) bp -> vf2 ( ) ; // 调用 base :: vf2 ( ) bp -> f ( ) ; // 调用 base :: f ( ) } ;
-
-
虚析构函数
-
构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数。
-
构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象
-
普通析构函数在删除动态派生类对象的调用情况
#include<iostream.h> class A { public: ~A(){ cout << "A::~A() is called.\n" ; } } ; class B : public A { public: ~B(){ cout << "B::~B() is called.\n" ; } } ; void main() { A *Ap = new B ; //用基类指针建立派生类的动态对象 B *Bp2 = new B ; //用派生类指针建立派生类的动态对象 cout << "delete first object:\n" ; delete Ap; //析构由基类指针建立的派生类对象,没有调用派生类析构函数 cout << "delete second object:\n" ; delete Bp2 ; //析构由派生类指针建立的派生类对象,正确调用派生类析构函数 } 输出: delete first object: A::~A() is called. delete second object: B::~B() is called. A::~A() is called.
-
虚析构函数在删除动态派生类对象的调用情况
#include<iostream.h> class A { public: virtual ~A(){ cout << "A::~A() is called.\n" ; } } ; class B : public A { public: ~B(){ cout << "B::~B() is called.\n" ; } } ; void main() { A *Ap = new B ; B *Bp2 = new B ; cout << "delete first object:\n" ; delete Ap; //正确调用派生类构造函数释放所有资源 cout << "delete second object:\n" ; delete Bp2 ; } 输出: delete first object: B::~B() is called. A::~A() is called. delete second object: B::~B() is called. A::~A() is called.
-
定义了基类虚析构函数,基类指针指向的派生类动态对象也可以正确地用delete析构
-
设计类层次结构时,提供一个虚析构函数,能够使派生类对象在不同状态下正确调用析构函数
-
-
纯虚函数和抽象类
-
纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本
-
纯虚函数为各派生类提供一个公共界面
-
纯虚函数说明形式:
virtual 类型 函数名(参数表)= 0 ;
-
一个具有纯虚函数的基类称为抽象类
-
class point { /*……*/ } ; class shape ; // 抽象类 { point center ; …… public : point where ( ) { return center ; } void move ( point p ) { enter = p ; draw ( ) ; } virtual void rotate ( int ) = 0 ; // 纯虚函数 virtual void draw ( ) = 0 ; // 纯虚函数 } ; ... shape x ; // error,抽象类不能建立对象 shape *p ; // ok,可以声明抽象类的指针 shape f ( ) ; // error, 抽象类不能作为返回类型 void g ( shape ) ; // error, 抽象类不能作为参数类型 shape & h ( shape &) ; // ok,可以声明抽象类的引用 class ab_circle : public shape { int radius ; public : void rotate ( int ) { } ; } ; 要使 ab_circle 成为非抽象类,必须作以下说明: class ab_circle : public shape { int radius ; public : void rotate ( int ) ; void draw ( ) ; } ; 并提供 ab_circle :: draw ( )和ab_circle :: rotate ( int ) 的定义
-
简单图形类
//figure.h class figure { protected : double x,y; public: void set_dim(double i, double j=0) { x = i ; y = j ; } virtual void show_area() = 0 ; }; class triangle : public figure { public : void show_area() { cout<<"Triangle with high "<<x<<" and base "<<y <<" has an area of "<<x*0.5*y<<"\n"; } }; class square : public figure { public: void show_area() { cout<<"Square with dimension "<<x<<"*"<<y <<" has an area of "<<x*y<<"\n"; } }; class circle : public figure { public: void show_area() { cout<<"Circle with radius "<<x; cout<<" has an area of "<<3.14*x*x<<"\n"; } }; #include<iostream.h> #include"figure.h" void main() { triangle t ; //派生类对象 square s ; circle c; t.set_dim(10.0,5.0) ; t.show_area(); s.set_dim(10.0,5.0) ; s.show_area() ; c.set_dim(9.0) ; c.show_area() ; } #include<iostream.h> #include"figure.h" void main() { figure *p; // 声明抽象类指针 triangle t; square s; circle c; p=&t; p->set_dim(10.0,5.0); // triangle::set_dim() p->show_area(); p=&s; p->set_dim(10.0,5.0); // square::set_dim() p->show_area(); p=&c; p->set_dim(9.0); // circle::set_dim() p->show_area(); }
-
使用抽象类引用
#include<iostream.h> class Number { public : Number (int i) { val = i ; } virtual void Show() = 0 ; protected: int val ; }; class Hex_type : public Number { public: Hex_type(int i) : Number(i) { } void Show() { cout << "Hexadecimal:" << hex << val << endl ; } }; class Dec_type : public Number { public: Dec_type(int i) : Number(i) { } void Show() { cout << "Decimal: " << dec << val << endl ; } }; class Oct_type : public Number { public: Oct_type(int i) : Number(i) { } void Show() { cout << "Octal: " << oct << val << endl ; } }; void fun( Number & n ) // 抽象类的引用参数 { n.Show() ; } void main() { Dec_type n1(50); fun(n1); //派生类对象调用函数 Dec_type::Show() Hex_type n2(50); fun(n2); // Hex_type::Show() Oct_type n3(50); fun(n3); // Oct_type::Show() }
-
-
虚函数与多态的应用
-
虚函数和多态性使成员函数根据调用对象的类型产生不同的动作
-
计算雇员工资
//Employee.h class Employee { public: Employee(const long,const char* ); virtual ~Employee(); const char * getName() const; const long getNumber() const; virtual double earnings() const=0; virtual void print() const; protected: long number; // 编号 char * name; // 姓名 }; //Employee.h class Employee { public: Employee(const long,const char* ); virtual ~Employee(); //虚析构函数 const char * getName() const; const long getNumber() const; virtual double earnings() const=0; //纯虚函数,计算月薪 virtual void print() const; //虚函数,输出编号、姓名 protected: long number; // 编号 char * name; // 姓名 }; // Manager.h class Manager : public Employee { public: Manager(const long , const char *, double =0.0); ~Manager() { } void setMonthlySalary(double); //置月薪 virtual double earnings() const; //计算管理人员月薪 virtual void print() const; //输出管理人员信息 private: double monthlySalary ; //私有数据,月薪 }; // HourlyWorker.h class HourlyWorker : public Employee { public: HourlyWorker(const long, const char *, double=0.0, int =0 ); ~HourlyWorker(){} void setWage(double); // 置时薪 void setHours(int); // 置工时 virtual double earnings() const; // 计算计时工月薪 virtual void print() const; // 输出计时工月薪 private: double wage; //时薪 double hours; //工时 }; // PieceWorker.h class PieceWorker : public Employee { public: PieceWorker(const long , const char *, double =0.0, int =0 ); ~PieceWorker() { } void setWage ( double ) ; // 置每件工件薪金 void setQuantity ( int ) ; // 置工件数 virtual double earnings() const;// 计算计件薪金 virtual void print() const;// 输出计件薪金 private: double wagePerPiece; // 每件工件薪金 int quantity; // 工件数 }; void test1() { cout << setiosflags(ios::fixed|ios::showpoint) << setprecision(2) ; Manager m1 ( 10135, "Cheng ShaoHua", 1200 ) ; Manager m2 ( 10201, "Yan HaiFeng"); m2.setMonthlySalary ( 5300 ) ; HourlyWorker hw1 ( 30712, "Zhao XiaoMing", 5, 8*20 ) ; HourlyWorker hw2 ( 30649, "Gao DongSheng" ) ; hw2.setWage ( 4.5 ) ; hw2.setHours ( 10*30 ) ; PieceWorker pw1 ( 20382, "Xiu LiWei", 0.5, 2850 ) ; PieceWorker pw2 ( 20496, "Huang DongLin" ) ; pw2.setWage ( 0.75 ) ; pw2.setQuantity ( 1850 ) ; // 使用抽象类指针,调用派生类版本的函数 Employee *basePtr; basePtr=&m1; basePtr->print();//调用函数语句形式相同 basePtr=&m2; basePtr->print(); basePtr=&hw1; basePtr->print(); basePtr=&hw2; basePtr->print(); basePtr=&pw1; basePtr->print(); basePtr=&pw2; basePtr->print(); }
-
-
异质链表
-
程序中,用基类类型指针,可以生成一个连接不同派生类对象的动态链表,即每个结点指针可以指向类层次中不同的派生类对象。这种结点类型不相同链表称为异质链表。
-
void AddFront( Employee * &h, Employee * &t ) // 在表头插入结点 { t->next = h ; h = t ; } void test3() // 测试函数 { Employee * empHead = NULL , * ptr; ptr = new Manager( 10135, "Cheng ShaoHua", 1200 ); // 建立第一个结点 AddFront( empHead, ptr ) ; // 插入表头 ptr = new HourlyWorker( 30712, "Zhao XiaoMing", 5, 8*20 ); // 建立第二个结点 AddFront( empHead, ptr ); // 插入表头 ptr = new PieceWorker ( 20382, "Xiu LiWei", 0.5, 2850 ); // 建立第三个结点 AddFront( empHead, ptr ) ; // 插入表头 ptr = empHead ; while( ptr ) { ptr -> print() ; ptr = ptr -> next ; } // 遍历链表,输出全部信息 ptr = empHead ; while( ptr ) // 遍历链表,输出姓名和工资 { cout << ptr -> getName() << " " << ptr -> earnings() << endl ; ptr = ptr -> next ; } }//Employee.h class Employee { public: Employee(const long,const char* ); virtual ~Employee(); const char * getName() const; const long getNumber() const; virtual double earnings() const=0; virtual void print() const; Employee *next ;// 增加一个指针成员 protected: long number; char * name; }; void AddFront( Employee * &h, Employee * &t ) // 在表头插入结点 { t->next = h ; h = t ; } void test3() // 测试函数 { Employee * empHead = NULL , * ptr; ptr = new Manager( 10135, "Cheng ShaoHua", 1200 ); // 建立第一个结点 AddFront( empHead, ptr ) ; // 插入表头 ptr = new HourlyWorker( 30712, "Zhao XiaoMing", 5, 8*20 ); // 建立第二个结点 AddFront( empHead, ptr ); // 插入表头 ptr = new PieceWorker ( 20382, "Xiu LiWei", 0.5, 2850 ); // 建立第三个结点,它们是不同类型的结点 AddFront( empHead, ptr ) ; // 插入表头 ptr = empHead ;//使用基类指针遍历链表 while( ptr ) { ptr -> print() ; ptr = ptr -> next ; } // 遍历链表,输出全部信息 ptr = empHead ; while( ptr ) // 遍历链表,输出姓名和工资 { cout << ptr -> getName() << " " << ptr -> earnings() << endl ; ptr = ptr -> next ; } }
-
5. 模版
-
函数模板
-
考虑求两参数之中大值函数:max ( a , b )对 a , b 的不同类型,都有相同的处理形式:
return ( a > b ) ? a : b ;
-
用已有方法解决对不同数据类型处理: (1)宏替换 # define max ( a , b ) ( a > b ? a : b ) 问题 避开类型检查 (2)重载 问题 需要许多重载版本 (3)使用函数模板
-
重载函数通常基于不同的数据类型实现类似的操作
-
对不同数据类型的操作完全相同,用函数模板实现更为简洁方便
-
声明模板中使用的类属参数。形式为 :
template < 类型形式参数表 >
-
template:关键字
-
类型形式参数的形式为:
typename T1 , typename T2 , …… , typename Tn
或class T1 , class T2 , …… , class Tn
-
例如 template < typename T > template < typename ElementType > template < typename NameType, typename DateType
-
-
函数模板与模板函数
-
函数模板声明
template < 类型形式参数表 > 类型 函数名 ( 形式参数表 ) { 语句序列 } //函数模板定义由模板说明和函数定义组成 //模板说明的类属参数必须在函数定义中至少出现一次 //函数参数表中可以使用类属类型参数,也可以使用一般类型参数
-
简单函数模板应用
#include <iostream.h> template < typename T > T max ( T a , T b ) { return a > b ? a : b ; } //函数模板 void main ( ) { cout << " max ( 3 , 5 ) is " << max ( 3 , 5 ) << endl ;//由实参类型实例化 cout << " max ( 'y' , 'e' ) is " << max ( 'y' , 'e' ) << endl ; cout << " max ( 9.3 , 0.5 ) is " << max ( 9.3 , 0.5 ) << endl ; }
-
冒泡排序法的函数模板
template <typename ElementType > //模板声明 void SortBubble ( ElementType *a , int size ) //类属参数 { int i, work ; ElementType temp ; //类属类型变量 for (int pass = 1; pass < size; pass ++ ) { work = 1; for ( i = 0; i < size-pass; i ++ ) if ( a[i] > a[i+1] ) { temp = a[i] ; a[i] = a[i+1] ; a[i+1] = temp ; work = 0 ; } if ( work ) break ; } }
-
-
重载函数模板
-
有些特殊情况需要函数模板参与重载
template < typename T > T max ( T a , T b ) { return a > b ? a : b ; } void f ( int i , char c ) { max ( i , i ) ; // ok max ( c , c ) ; // ok max ( i , c ) ; // error,无法匹配 max ( c , i ) ; // error,模板类型不能提供类型的隐式转换 } template < typename T > T max ( T a , T b ) { return a > b ? a : b ; } int max ( int a , int b ) // 模板函数重载版本 { return a > b ? a : b ; } void f ( int i , char c ) { max ( i , i ) ; // ok max ( c , c ) ; // ok max ( i , c ) ; // ok ,由系统提供隐式转换 max ( c , i ) ; // ok }
-
重载函数模板示例
#include <iostream.h> #include <string.h > template <typename T> //函数模板 T Max( const T a, const T b ) { return a>b ? a : b ; } template <typename T> //重载函数模板 T Max( const T a, const T b , const T c) { T t ; t = Max(a, b) ; return Max ( t, c ) ; } int Max( const int a , const char b ) { return a>b ? a : b ; }//用普通函数重载函数模板 void main ( ) { cout<< " Max( 3, 'a' ) is " << Max( 3, 'a' ) << endl ; cout << " Max(9.3, 0.5) is " << Max(9.3, 0.5) << endl ; cout << " Max(9, 5, 23) is " << Max(9, 5, 23) << endl ; }
-
-
类模板
- 模板用于实现类所需数据的类型参数化
- 类模板在表示如数组、表、图等数据结构显得特别重要,这些数据结构的表示和算法不受所包含的元素类型的影响
-
类模板与模板类
-
类模板由模板说明和类说明构成
template <类型形式参数表> 类声明 例如 template< typename Type > class TClass { // TClass的成员函数 private : Type DateMember ; //类属参数必须至少在类说明中出现一次 //… };
-
一个数组类模板
template< typename T > class Array { public : Array ( int s ) ; virtual ~ Array () ; virtual const T& Entry( int index ) const ; virtual void Enter( int index, const T & value ) ; protected : int size ; T * element ; //数据成员是 T 类型指针 } ; //类模板的成员函数是 函数模板 template<typename T> Array<T>::Array(int s) { if ( s > 1 ) size = s ; else size = 1 ; element = new T [ size ] ; } template < typename T > Array < T > :: ~Array() { delete [] element ; } template < typename T > const T& Array < T > :: Entry ( int index ) const { return element [ index ] ; } template < typename T > void Array < T > :: Enter(int index, const T& value) { element [ index ] = value ; } #include <iostream.h> #include "Array.h" void main() { Array <int> IntAry( 5 ) ; int i ; for ( i = 0; i < 5; i ++ ) IntAry.Enter ( i, i ) ; cout << "Integer Array : \n" ; for ( i = 0; i < 5; i ++ ) cout << IntAry.Entry(i) << '\t' ; cout<<endl ; Array <double> DouAry( 5 ) ; for ( i = 0; i < 5; i ++ ) DouAry.Enter ( i, (i+1)*0.35 ) ; cout << "Double Array : \n" ; for ( i = 0; i < 5; i ++ ) cout << DouAry.Entry(i) << '\t' ; cout<<endl; }
-
-
类模板作函数参数
-
函数的形式参数类型可以是类模板或类模板的引用,对应的实际参数是该类模板实例化的模板类对象
-
当一个函数拥有类模板参数时,这个函数必定是函数模板
-
一个用 Array<T> 作参数的函数模板 template < typename T > void Tfun( const Array <T> & x , int index ) { cout << x.Entry( index ) << endl ; } 调用函数模板 Array <double> DouAry( 5 ) ; … Tfun(DouAry,3); 1.建立对象 class Array { public : Array ( int s ) ; virtual ~ Array () ; virtual const double & Entry( int index ) const ; virtual void Enter( int index, const double & value ) ; private: int size ; double * element ; } ; 2.调用函数 调用构造函数,实例化模板类,建立对象 virtual const double & Entry( int index ) const ;//实例化为模板函数
-
-
在类层次中的类模板
-
个类模板在类层次结构中既可以是基类也可以是派生类:
- 类模板可以从模板类派生
- 类模板可以从非模板类派生
- 模板类可以从类模板派生
- 非模板类可以从类模板派生
-
从类模板Array派生一个安全数组类模板BoundArray
template< typename T > class Array { public : Array ( int s ) ; virtual ~ Array () ; virtual const T& Entry( int index ) const ; virtual void Enter( int index, const T & value ) ; protected : int size ; T * element ; } ; template < typename T > class BoundArray : public Array < T > { public : BoundArray ( int low = 0, int height = 1 ) ; virtual const T& Entry ( int index ) const ; virtual void Enter ( int index , const T& value ) ; private: int min ; } ;
-
-
在类层次中的类模板
-
类模板派生普通类,在定义派生类时要对基类的抽象类参数实例化
-
从普通类派生模板类,意味着派生类添加了抽象类数据成员
-
从类模板A派生普通类B
#include<iostream.h> template< typename T > //定义类模板 class A { public : A( T x ) { t = x ; } void out() { cout << t << endl ; } protected : T t ; } ; class B: public A<int> //派生一般类 { public : B ( int a, double x ) : A <int> ( a ) { y = x ; }//实例化基类抽象类型参数 void out() { A <int> :: out() ; cout << y << endl ; } protected : double y ; }; void main() { A <int> a( 123 ) ; a.out() ; B b ( 789, 5.16 ) ; b.out() ; }
-
-
类模板与友员
-
在类模板中可以声明各种友员关系
- 一个函数或函数模板可以是类或类模板的友员
- 一个类或类模板可以是类或类模板的友员类
- 声明这种模板之间的友员关系符号比较烦琐
-
template <typename T> class X { //…… friend void f1(); } 函数f1成为类模板X实例化的每个模板类的友员函数 template <typename T> class X { //…… friend void f2( X<T> & ); } 对特定类型(如double),使模板函数f2(X<double>&)成为X<double>的友员 template <typename T> class X { //…… friend void A::f3(); } A类的成员函数f3成为类模板X实例化的每个模板类的友员函数 template <typename T> class X { //…… friend void B<T>::f4( X<T> & ); } 对特定类型(如double),使模板类B<double>的成员函数f4(X<double>&)成为模板类X<double>的友员
-
-
类模板与static成员
-
从类模板实例化的每个模板类有自己的类模板数据成员,该模板类的所有对象共享一个static数据成员
-
和非模板类的static数据成员一样,模板类的static数据成员也应该在文件范围定义和初始化
-
每个模板类有自己的类模板的static数据成员副本
-
#include<iostream.h> const double pi=3.14159; template<typename T> class Circle { T radius ; static int total; //类模板的静态数据成员 public : Circle(T r=0) { radius = r ; total++; } void Set_Radius( T r ) { radius = r ; } double Get_Radius() { return radius ; } double Get_Girth() { return 2 * pi * radius ; } double Get_Area() { return pi * radius * radius ; } static int ShowTotal(); //类模板的静态成员函数 } ; template<typename T> int Circle<T>::total=0; template<typename T> int Circle<T>::ShowTotal(){ return total; } { Circle<int> A, B ; A.Set_Radius( 16 ) ; cout << "A.Radius = " << A.Get_Radius() << endl ; cout << "A.Girth = " << A.Get_Girth() << endl ; cout << "A.Area = " << A.Get_Area() << endl ; B.Set_Radius( 105 ) ; cout << "B.radius = " << B.Get_Radius() << endl ; cout << "B.Girth=" << B.Get_Girth() << endl ; cout << "B.Area = " << B.Get_Area() << endl ; cout<<"Total1="<<Circle<int>::ShowTotal()<<endl;//A.ShowTotal()或 B.ShowTotal() Circle<double> X(6.23), Y(10.5), Z(25.6); cout << "X.Radius = " << X.Get_Radius() << endl ; cout << "X.Girth = " << X.Get_Girth() << endl ; cout << "X.Area = " << X.Get_Area() << endl ; cout << "Y.radius = " << Y.Get_Radius() << endl ; cout << "Y.Girth=" << Y.Get_Girth() << endl ; cout << "Y.Area = " << Y.Get_Area() << endl ; cout << "Z.Girth=" << Z.Get_Girth() << endl ; cout << "Z.Area = " << Z.Get_Area() << endl ; cout<<"Total2="<<Circle<double>::ShowTotal()<<endl; //X.ShowTotal()或 Y.ShowTotal()
-