C++通过类派生(Class Derivation)的机制支持继承(Inheritance)。允许程序员在保持原有类特性的基础上进行扩展,增加功能,派生出新类。
继承的方式有以下2种:单一继承和多重继承。
派生类的定义中包括子类新增加的成员和继承父类需要重写的成员。C++允许在派生类中重新声明和定义这些成员函数,使这些函数具有新的功能,称之为重写或覆盖。重写函数起屏蔽、更新作用,取代基类成员,完成新功能。
测试一:子类继承基类的所有属性和函数,设基类函数和数据均为public类型,并且以public方式被继承,做如下测试。例如:
// test.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include <string> using namespace std; class CBox { public: CBox() { cout<<"调用CBox类的构造函数;"<<endl; } void SetLength(double len) //设置盒子的长 { cout<<"CBox类中设置盒子的长;"<<endl; length = len; } void SetWidth(double w) //设置盒子的宽 { cout<<"CBox类中设置盒子的宽;"<<endl; width = w; } void SetHeight(double h) //设置盒子的高 { cout<<"CBox类中设置盒子的高;"<<endl; heigth = h; } void ShowBox() //显示盒子长、宽、高的成员函数 { cout<<"CBox类中显示盒子数据;"<<endl; cout<<"length="<<length<<endl <<"width="<<width<<endl <<"heigth="<<heigth<<endl; } ~CBox() { cout<<"调用CBox类的析构函数;"<<endl; } public: double length,width,heigth; }; class CColorbox:public CBox { public: CColorbox() { cout<<"调用CColorbox类的构造函数;"<<endl; } ~CColorbox() { cout<<"调用CColorbox类的析构函数;"<<endl; } }; void test() { CColorbox colorbox; colorbox.SetHeight(10); colorbox.SetWidth(5); colorbox.SetLength(20); colorbox.ShowBox(); } int _tmain(int argc, _TCHAR* argv[]) { test(); system("pause"); return 0; }运行结果:
首先调用基类的构造函数,再调用子类构造函数。析构时先析构子类后析构基类。子类继承了基类的所有公有函数和成员,除了基类的构造、析构函数。
测试二:子类重写基类的函数
子类中重写基类的函数后,默认调用的是子类的函数!!!
1、子类中添加基类中同名但不同参数的函数,则基类的带参数的Setheight(double h)函数首先被调用!!!
则编译出错test.cpp(72): error C2660: “CColorbox::SetHeight”: 函数不接受 1 个参数:
class CColorbox:public CBox { public: CColorbox() { cout<<"调用CColorbox类的构造函数;"<<endl; } void SetHeight() //新添加的 { cout<<"重载:调用CColorbox类的与基类同名但不同参数的SetHeight函数;"<<endl; } ~CColorbox() { cout<<"调用CColorbox类的析构函数;"<<endl; } }; void test() { CColorbox colorbox; colorbox.SetHeight(10); colorbox.SetHeight(); //新添加的 colorbox.SetWidth(5); colorbox.SetLength(20); colorbox.ShowBox(); }
,才能够对其(子类继承的函数)进行重载。
class CColorbox:public CBox { public: CColorbox() { cout<<"调用CColorbox类的构造函数;"<<endl; } void SetHeight(double h) //新添加。删除此函数,则子类中就不存在SetHeight() { cout<<"调用CColorbox类的与基类同名同参数的SetHeight函数;"<<endl; heigth = h; } void SetHeight() { cout<<"重载:调用CColorbox类的与基类同名但不同参数的SetHeight函数;"<<endl; } ~CColorbox() { cout<<"调用CColorbox类的析构函数;"<<endl; } };调用结果:子类中重写基类的函数后,默认调用的是子类的函数!!!
3、在子类中添加新的函数和数据成员,调用子类新功能:
class CColorbox:public CBox { public: CColorbox() { cout<<"调用CColorbox类的构造函数;"<<endl; } void SetHeight(double h) //删除此函数,则子类中就不存在SetHeight() { cout<<"调用CColorbox类的与基类同名同参数的SetHeight函数;"<<endl; heigth = h; } void SetHeight() { cout<<"重载:调用CColorbox类的与基类同名但不同参数的SetHeight函数;"<<endl; } void SetColor(int c) { cout<<"CColorbox类中设置盒子的颜色;"<<endl; color = c; } void ShowColBox() { cout<<"调用CColorbox类中ShowColBox();"<<endl; cout<<"color="<<color<<endl; //访问自己的私有成员 cout<<"length"<<length<<endl; } ~CColorbox() { cout<<"调用CColorbox类的析构函数;"<<endl; } public: int color; }; void test() { CColorbox colorbox; colorbox.SetHeight(10); colorbox.SetHeight(); colorbox.SetWidth(5); colorbox.SetLength(20); colorbox.SetColor(1); colorbox.ShowBox(); colorbox.ShowColBox(); }运行结果:
三、数据成员的重载
因为成员重载后,基类和子类中各有一份相同的数据成员,调用那一个成员就成了一个需要讨论的问题,测试时采用对于类成员的显式调用完成显示。
实验结果证明:基类函数会调用基类的数据成员,子类的成员函数默认调用子类的数据成员!!!
class CColorbox:public CBox { public: CColorbox() { cout<<"调用CColorbox类的构造函数;"<<endl; } void SetColLength(double len) //子类设置长度函数 { cout<<"CColorbox类中设置盒子的长;"<<endl; length = len; } void ShowColBox() //显示盒子长、宽、高的成员函数 { cout<<"显示基类长度;"<<endl; cout<<"length = "<<CBox::length<<endl; cout<<"显示子类长度;"<<endl; cout<<"length = "<<CColorbox::length<<endl; } ~CColorbox() { cout<<"调用CColorbox类的析构函数;"<<endl; } public: int length; //添加同名成员变量 }; void test() { CColorbox colorbox; colorbox.SetLength(20); //调用基类函数设置长度 colorbox.SetColLength(10); //调用子类函数设置长度 colorbox.ShowColBox(); }显示运行结果:
1、若将test中调用基类的赋值函数注释掉,则显示基类长度中,成员变量为随机值;
2、若要调用基类的长度成员,则要进行显式引用,即CBox::length。
派生类的生成
仔细分析派生新类这个过程,实际是经历了以下步骤:
第一步是继承基类的成员,不论是数据成员,还是成员函数,除构造函数与析构函数外全部接收(即全部拷贝,虽然子类中覆盖后只是显示调用,但仍可调用),全部成为派生类的成员。
子类中访问基类成员的拷贝方式为:子类对象.基类名::基类成员。如:
colorbox.CBox::length。
第二步是重写基类成员。如果派生类声明了一个与基类成员函数相同的成员函数时,派生类中的新成员则屏蔽了基类同名成员,类似函数中的局部变量屏蔽全局变量。称为同名覆盖(Override)。
由code1分析:
1、子类中重写了基类中的同名函数,则基类的同名函数不会再出现在子类。
1)重写:void SetHeight(double h);或void SetHeight();则基类的void SetHeight(double h)函数将不能在子类中显式调用。 2)若子类中需要重载SetHeight函数,则需要先重写与基类同参、同名、同返回值的函数,再进行重载。
2、子类中重写了基类中的同名数据,则该数据成员在子类中也存在了一份拷贝,如 length,则,默认方式是:继承过来的基类函数调用基类中的拷贝length,若用子类函数则调用子类中的length。
第三步是定义新成员。新成员必须与基类成员不同名,是派生类自己的新特性。派生类新成员的加入使得派生类在功能上有所发展。这一步是继承与派生的核心特征。
第四步是重写构造函数与析构函数。因为派生类不继承基类的构造函数与析构函数,并且派生类的需要对新添加的数据成员进行必要的初始化,所以构造函数与析构函数需要重写。
原文地址:http://blog.csdn.net/xaut_zjb/article/details/40947589