【c++】继承

来源:互联网 发布:苹果电脑mac地址 编辑:程序博客网 时间:2024/05/01 11:50

C++ 继承

面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行时间的效果。

进入正题之前我们来说一说”关系“

has-a 午餐可以有苹果,但是苹果不是午餐,所以苹果是午餐的一部分,我叫has-a
is-like-a 人们通常说律师是鲨鱼,但是律师不是鲨鱼,只是律师像鲨鱼
is-implemented-as-a (作为什么来实现)可以用数组来实现栈,但是从array派生stack不合适,因为栈不是数组
uses-a 计算机可以使用打印机
is-a 苹果和香蕉都是水果,

而继承代表了 is a 关系。例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物,等等。。

基类 & 派生类

当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。

一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:

class derived-class: access-specifier base-class
其中,访问修饰符 access-specifier 是 public、protected 或 private 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。
假设有一个基类 Shape,Rectangle 是它的派生类,如下所示:

#include <iostream>using namespace std;// 基类class Shape {   public:      void setWidth(int w)      {         width = w;      }      void setHeight(int h)      {         height = h;      }   protected:      int width;      int height;};// 派生类class Rectangle: public Shape{   public:      int getArea()      {          return (width * height);       }};int main(void){   Rectangle Rect;   Rect.setWidth(5);   Rect.setHeight(7);   // 输出对象的面积   cout << "Total area: " << Rect.getArea() << endl;   return 0;}

当上面的代码被编译和执行时,它会产生下列结果:
Total area: 35

访问控制和继承

派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。
我们可以根据访问权限总结出不同的访问类型,如下所示:

访问 public protected provate 同一个类 yese yes yes 派生类 yse yes no 外部类 yes no no

一个派生类继承了所有的基类方法,但下列情况除外:

  • 基类的构造函数、析构函数和拷贝构造函数。-
  • 基类的重载运算符。
  • 基类的友元函数。

继承类型

当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。
我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:

  • 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
  • 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
  • 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。

派生类构造函数

调用派生类构造函数时程序会按下列顺序进行初始化
1. 进入派生类构造函数的第一步是调用上一层基类的构造函数
2. 进入上一层基类的构造后第一步是调用上上一层基类的构造函数
3. 一直到根进入基类的构造函数时,先初始化根基类中定义的变量再运行根基类构造函数中的内容。运行完毕之后跳回前一层派生类的构造函数,然后初始化这个派生类中定义的变量再运行构造函数中的内容,运行完毕之后跳会之前前一层派生类的构造函数
4. 直到回到一开始的派生类构造函数,同样先出初始化类中的的变量,再运行构造函数中的内容。构造函数运行完毕后对象也就创建完毕了

这就有出现三个问题了
1每一层构造函数要先赋予变量初值,再运行构造函数中的初始化语句,效能损耗
2我们不能指定哪个基类的构造函数
3不能指定调用基类的构造函数意味着我们不能精确控制基类的私有数据内容。

成员初始化列表

但是c++给我提供了成员初始化列表来解决上面的问题
Son::Son():Father(),son_var(233){};//中间用逗号分开
上面明确表示调用Father()这个构造函数,并且派生类中son_var赋值为233。

示例

#include <iostream>#include<cstring>using std::string ;using std::cout;using std::endl;class Father{    public :        const string   last_name;        void Father_initialize(double ,int ,const string ,string );             Father();        Father(string);    protected :        double assets;    private :        int age;        const string   frist_name;        string secret;};Father::Father():last_name("zhang"){    cout<<"The parameter construction of the 'Father'class is called"<<endl;    Father_initialize(10000,35,"san","have not");}Father::Father(string last_name):last_name(last_name){    cout<<"The parameter construction of the 'Father' class is called"<<endl;     Father_initialize(10000,35,"san","have not");}void Father::Father_initialize(    double assets,int age,const string frist_name,string secret){    this->assets=assets;    this->age=age;    this->frist_name;    this->secret=secret;}class Son:public Father{    public :        Son();        Son(string  ,int);        void f(string);    protected :    private :        int age;        const std::string   frist_name;        string secret;};Son::Son(){    cout<<"The parameter construction of the 'Son'class is called"<<endl;}Son::Son(string frist_name,int age ):frist_name(frist_name),age(age),Father("zhang"){    cout<<"The parameter construction of the 'Son' class is called"<<endl;}int main(int argc, char** argv) {    Son son2;    Son son("small san",18);    return 0;}

输出结果
The parameter construction of the ‘Father’class is called
The parameter construction of the ‘Son’class is called
The parameter construction of the ‘Father’ class is called
The parameter construction of the ‘Son’ class is called

切记c++中的成员初始化列表只能初始化变量和构造函数,不同的函数是不可以调用的

示例:

#include <iostream>#include<cstring>using std::string ;using std::cout;using std::endl;int ar[3]={};class Test{    public :        void print_abc ();        void print_def();        Test();        Test(int  ) ;        Test(int,int ) ;        Test(int , int ,int) ;        Test(int , int ,int,int ) ;        Test(int , int ,int,int ,int) ;    private:        int a;        int b;        int c;          int f;        int d;        int e;};void Test::print_abc(){     cout<<"a "<<"b "<<"c "<<endl;    cout<<ar[0]<<' '<<ar[1]<<' '<<ar[2]<<' '<<endl;}void Test::print_def(){     cout<<"d "<<"e "<<"f "<<endl;    cout<<ar[0]<<' '<<ar[1]<<' '<<ar[2]<<' '<<endl;}Test::Test() {    ar[0]=a;    ar[1]=b;    ar[2]=c;    print_abc();}Test::Test(int ) :a(10),b(10),c(a+b){    ar[0]=a;    ar[1]=b;    ar[2]=c;    print_abc();}Test::Test(int  ,int ) :c(a+b),a(10),b(10){    ar[0]=a;    ar[1]=b;    ar[2]=c;    print_abc();}Test::Test(int , int ,int ){    ar[0]=d;    ar[1]=e;    ar[2]=f;    print_def();}Test::Test(int , int ,int,int ) :d(10),e(10),f(d+e){    ar[0]=d;    ar[1]=e;    ar[2]=f;    print_def();} Test::Test(int , int ,int,int ,int) :f(d+e),d(10),e(10){    ar[0]=d;    ar[1]=e;    ar[2]=f;    print_def();}int main(int argc, char** argv) {    Test test1;    Test test2(1);    Test test3(1,1);    Test test4(1,1,1);    Test test5(1,1,1,1);    Test test6(1,1,1,1,1);    return 0;}

输出结果为
a b c
0 0 21
a b c
10 10 20
a b c
10 10 20
d e f
8 0 0
d e f
10 10 48
d e f
10 10 4254640

由上面的输出结果我们可以知道成员列表初始化的先后顺序不取决于:后面的顺序,取决于变量定义的顺序

其他默认生成的函数

造函数:先调用派派生类的默认构造函数后调用基类默认的构造函数。
构函数:先调用派生类的构造函数,在调用基类的构造函数
复制构造函数:同构造函数。
=运算符重载函数:同构造函数。

多继承

多继承即一个子类可以有多个父类,它继承了多个父类的特性。 类可以从多个类继承成员,语法如下:

class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…{<派生类类体>};

其中访问修饰符继承方式是 public、protected 或 private 其中的一个,用来修饰每个基类,各个基类之间用逗号分隔,如上所示。现在让我们一起看看下面的实例:

#include <iostream>using namespace std;// 基类 Shapeclass Shape {   public:      void setWidth(int w)      {         width = w;      }      void setHeight(int h)      {         height = h;      }   protected:      int width;      int height;};// 基类 PaintCostclass PaintCost {   public:      int getCost(int area)      {         return area * 70;      }};// 派生类class Rectangle: public Shape, public PaintCost{   public:      int getArea()      {          return (width * height);       }};int main(void){   Rectangle Rect;   int area;   Rect.setWidth(5);   Rect.setHeight(7);   area = Rect.getArea();   // 输出对象的面积   cout << "Total area: " << Rect.getArea() << endl;   // 输出总花费   cout << "Total paint cost: $" << Rect.getCost(area) << endl;   return 0;}

当上面的代码被编译和执行时,它会产生下列结果:

Total area: 35
Total paint cost: $2450

0 0
原创粉丝点击