类之间的关系(2. 继承(Inheritance)关系-1)

引言

在之前的章节中,我们定义了人类。现在,我们需要创建学生对象,因为每个学生除了具有人类的属性和方法,还有年级属性以及设置和读取年级的方法。我们可以从头编写学生类,不过可以利用C++的继承语法,让学生类继承于人类。这样每个学生对象不但有新定义的属性和方法,还继承了人类的属性(数据成员)和方法(函数成员)。

#include <iostream>
#include <string>
using namespace std;

class Human{    
    public:
        // 定义人类函数成员,描述了人类的行为
        void introduce();

        void read();

        void set_name(string n){name = n;}
        string get_name(){return name;}

        void set_age(int a){age = a;}
        int get_age(){return age;}

        void set_id(string i){id = i;}
        string get_id(){return id;}

        void set_male(bool i_m){is_male = i_m;}
        bool get_male(){return is_male;}

        void init(string n, bool is_m, int a, string i){
            name = n;
            is_male = is_m;
            age = a;
            id = i;
        }  

        // 定义人类数据成员的组成,描述了人类的属性
        string name;
        bool is_male;        
        int age;
        string id;            
};

void Human::introduce(){
    cout << "大家好,我是" << name << endl;
    if(is_male){
         cout << "男性" << endl;
    }else{
         cout << "女性" << endl;
    }
    cout << age << "岁" << endl;
    if(id.length() == 0){            
        cout << "身份证号未知" << endl;
    }else{
        cout << "身份证号:" << id << endl;
    }            
}      

void Human::read()  // 新加成员方法
{
     cout << "请输入姓名:";
     cin >> name;
     cout << "请输入是否为男性(1/0):";
     cin >> is_male;
     cout << "请输入年龄:";
     cin >> age;
     cout << "请输入身份证号:";
     cin >> id;
}

class Student : public Human
{
     public:
         void set_grade(int g){ grade = g; }
         int get_grade(){ return grade; }

         int grade;          
};

int main()
{
    Student s;
    s.init("张三", true, 20, "123456");  // 从Human类继承下来的方法
    s.set_grade(1); // Student类添加的新方法
    return 0;
}

因为Student类继承于Human类,Human类称为父类(基类),Student称为子类(派生类)。

A2继承于B2,用UML图展示

继承的特性

继承让子类对象包含了父类的数据和函数成员,因此在需要父类对象的地方,我们可以使用子类对象。

父类的成员被子类继承(子承父业)

继承让子类对象含有了父类的所有数据成员和函数成员。

#include <iostream>

using namespace std;

class Base
{
   public:
       int x;
       void show_x(){ cout << x << endl;}
};

class Derived : public Base
{
   public:
       int y;
       void show_y(){ cout << y << endl;}
};

int main()
{
    Derived d;

    d.x = 100; // 父类的数据成员,子类继承下来了
    d.y = 200;

    d.show_x();  // 父类的函数成员,子类也继承下来了
    d.show_y();

    cout << "sizeof(Base):" << sizeof(Base) << endl;
    cout << "sizeof(Derived):" << sizeof(Derived) << endl;

    return 0;
}

运行结果:

这里写图片描述

Derived类继承于Base类:

这里写图片描述

Derived类“真实”类图:

这里写图片描述

子类对象可当成父类对象来使用(父职子当)

正是由于子类对象含有了父类的所有数据成员和函数成员,因此在需要父类对象的地方,可以使用子类对象。在使用者的眼中,子类对象可以当成父类对象来使用。

反之,父类对象是不能当成子类对象来使用的。父类对象没有子类对象新加的那些成员。

#include <iostream>

using namespace std;

class Base
{
   public:
       int x;
       void show_x(){ cout << "Base, x:" << x << endl;}
};

class Derived : public Base
{
   public:
       int y;
       void show_y(){ cout << "Derived, y:" << y << endl;}
};

void use_base(Base b)
{
    b.show_x();
}

void use_base_pointer(Base *p_b)
{
    p_b->show_x();
}

void use_base_reference(Base &b)
{
    b.show_x();
}

int main()
{

    Derived d;

    d.x = 100;
    d.y = 200;

    Base b;    
    b = d; // 赋值号右边需要一个Base对象,可以使用Derived对象d
    b.show_x();
    use_base(d); // 参数需要一个Base对象,可以使用Derived对象d

    cout << endl;

    Base *p_b = NULL;
    p_b = &d;  // 赋值号右边需要一个Base对象的地址,可以使用Derived对象d的地址
    p_b->show_x();
    use_base_pointer(&d);  // 参数需要一个Base对象的地址,可以使用Derived对象d的地址

    cout << endl;

    Base &r_b = d;  // 赋值号右边需要一个Base对象,可以使用Derived对象d
    r_b.show_x(); 
    use_base_reference(d); // 参数需要一个Base对象,可以使用Derived对象d

    cout << endl;

    system("pause");

    return 0;
}

正是因为子类对象可以当成父类对象来使用,因此称子类和父类之间存在“是一个(is a)”的关系,即子类对象“是一个”父类对象。

继承和组合的联系和区别

如前所述,组合关系本身表达了“有一个”的关系,即包含关系,而继承也具有这种特点。

#include <iostream>

using namespace std;

class Base
{
   public:
       int x;
       void show_x(){ cout << x << endl;}
};

class Derived : public Base  
{
   public:
       int y;
       void show_y(){ cout << y << endl;}
};

class Composition
{
   public:
      Base b;
      int z;
      void show_z(){ cout << z << endl;}
};

int main()
{
    Derived d;
    Composition c;

    d.x = 100;
    d.show_x();

    c.b.x = 200;
    c.b.show_x();

    cout << "sizeof(Base):" << sizeof(Base) << endl;
    cout << "sizeof(Derived):" << sizeof(Derived) << endl;
    cout << "sizeof(Composition)" << sizeof(Composition) << endl;

    return 0;   
}

运行结果:

这里写图片描述

可见,Derived对象和Composition对象都包含了Base对象。因此继承也有组合的含义。那继承和组合的差别在哪里呢,实际编程中是使用组合还是继承呢?

  • 继承关系下,子类对象本身包含了父类所有成员,因此对外来说,子类对象是一个父类对象,子类对象可当成父类对象来使用。(Derived对象是一个Base对象,可以当成Base对象来使用)
  • 组合关系下,并不具有这种“是一个”的关系。Composition对象本身并没有包含Base类的成员,因此不能当成Base对象来使用。Composition对象的一个成员b是Base类的,它们之间仅仅是“有一个”的关系。

因此,我们说组合表达的是类之间的“有一个”关系,继承表达了更深一层的“是一个”的关系。

再以Human类、Student类为例:

  • Student类继承自Human类,而不是包含一个成员是Human类。这是因为学生和人之间在概念上是“是一个”的关系,很可能需要学生类支持人类支持的各种方法,需要处理Human对象的代码对Student对象也可用。例如:

    bool is_older(Human p1, Human p2)
    {
        if(p1.get_age() > p2.get_age()){
            return true;
        }else{
            return false;
        }
    } 
    
    int main()
    {
        Student s1, s2;
    
        s1.init("张三", true, 20, "123456");
        s1.set_grade(1);
        s2.init("李四", true, 21, "654321");
    
        bool result = is_older(s1, s2); // Student是一个Human
    
        return 0;
    }
    
    
  • Human类有一个成员name是string类的,而不是继承自string类。这是因为在人和姓名的关系是“有一个”,而不是“是一个”,没有必要让Human继承自string类。

总结一下,程序中已经有A类了,需要添加B类,那么,

  • B类仅仅使用A类的功能,不需要用A类对象保存B类的状态:B类使用A类,使用(依赖)关系

  • B类的部分属性需要保存在A类,不需要被当成A类那样来使用:B类一个成员是A类,组合(关联)关系

  • B类可能被当成A类来使用:B类继承于A类,继承关系

语法

语法可参考类之间的继承(Inheritance between classes)

使用注意

定义子类时,class Derived : public Base中不要忘记写public,这影响到父类成员在子类以及类外的访问权限。有关知识下节会涉及。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值