引言
在之前的章节中,我们定义了人类。现在,我们需要创建学生对象,因为每个学生除了具有人类的属性和方法,还有年级属性以及设置和读取年级的方法。我们可以从头编写学生类,不过可以利用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
,这影响到父类成员在子类以及类外的访问权限。有关知识下节会涉及。