在<3> 面向对象到底有社么(上)一文中,我们说了下关于面向对象和面向过程的区别和优劣性。在面向对象里边,也谈了下关于“面向对象三大特性”的其中一个特性“封装性”的简单理解。在这一文中,我们主要一起来了解下关于面向对象的第二个特性“继承”的理解,一起来看下“继承”到底是什么?继承到底有什么作用?以及“继承”在什么情况下会用到?继承的优点在哪里?
2.2 ”继承“到底是什么?为什么要有它这个东西?
我是觉得,现在的名称翻译还是挺符合“顾名思义”的原则的,继承在我们一般人的眼里,就代表了“依法拿到遗产”、“把前人的文化、知识传承过来”等意思,在高级程序设计语言这里,其实也有类似的意味。
还是接着我们上一篇文章的“Person”类作为例子,接着说。实际上,在我们的项目中,“人”只是一个比较宽泛的“类别”,在这个社会群体中,“学生”,“老师”,“校长”等等社会职业属于“人”这个类型,但是这些社会职业本身也都是单独的“类别”。假设:我们在“Person”类里边添加关于“学生”,“老师”,“校长”的一些信息,结果会是这样的:
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 类的封装
class Person{
public:
Person(int age, char gender, std::string name)
:_age(age),
_gender(gender),
_name(name)
{
_score = new double[10];
}
~Person();
public:
int _age;
char _gender;
std::string _name;
// 学生
double *_score;
std::string _studentID;
std::string _major;
// 老师
std::vector<std::string> _lesson;
std::string _teacherID;
// 校长
std::string _school;
//···
//···
//···
public:
const int get_age() { return _age; }
const char get_gender() { return _gender; }
const std::string get_name() { return _name; }
void set_age(const int &value);
void set_gender(const char &value);
void set_name(const std::string &value);
};
void Person::set_age(const int &value){
this->_age = value;
}
void Person::set_gender(const char &value) {
this->_gender = value;
}
void Person::set_name(const std::string &value) {
this->_name = value;
}
Person::~Person()
{
//
}
int main() {
// 声明一个Person类型的对象
// 请注意:对象的概念在这里开始出现,因为Person是一个集合,一个类型,那么类型的一个一个实际的例子,就是对象
Person m_person(25, '男', "chengzhen");
// 获得年龄
m_person.get_age();
// 获得性别
m_person.get_gender();
// 获得姓名
m_person.get_name();
// 设置年龄
m_person.set_age(30);
// 设置性别
m_person.set_gender('nv');
// 设置姓名
m_person.set_name("Charmain");
std::cin.get();
}
如果我们在某个地方只用学生这个“类别”,我们就不得不来一次“Person student(25,'男',“who”)“一次,但是我们发现,在创建一个对象并初始化的时候,类已经为我们创建好了”老师“,”校长“等社会角色身上存在的一些特性(老师教授的”课“有哪些,校长管理的是”哪个学校“等等)。而我们明白的是,学生并不需要知道具备这些特性,因为他们只需要做好自己,了解自己的特性就好。由于我们把这些内容都融合在一个类里边,所以每次在创建对象的时候,就会有多余的数据被创建,比如:std::vector<std::string> _lesson、std::string _school等等,造成代码冗余,工程文件过大,不易管理、可读性不高。当然,我们也可以这么来写设计类:
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 类的封装
class Person{
public:
Person(int age, char gender, std::string name)
:_age(age),
_gender(gender),
_name(name)
{}
~Person();
public:
int _age;
char _gender;
std::string _name;
public:
const int get_age() { return _age; }
const char get_gender() { return _gender; }
const std::string get_name() { return _name; }
void set_age(const int &value);
void set_gender(const char &value);
void set_name(const std::string &value);
};
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 类的封装
class Student {
public:
Student(int age, char gender, std::string name)
:_age(age),
_gender(gender),
_name(name)
{
_score = new double[10];
}
~Student();
public:
int _age;
char _gender;
std::string _name;
// 学生
double *_score;
std::string _studentID;
std::string _major;
public:
const int get_age() { return _age; }
const char get_gender() { return _gender; }
const std::string get_name() { return _name; }
void set_age(const int &value);
void set_gender(const char &value);
void set_name(const std::string &value);
};
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 类的封装
class Teacher {
public:
Teacher(int age, char gender, std::string name)
:_age(age),
_gender(gender),
_name(name)
{}
~Teacher();
public:
int _age;
char _gender;
std::string _name;
// 老师
std::vector<std::string> _lesson;
std::string _teacherID;
public:
const int get_age() { return _age; }
const char get_gender() { return _gender; }
const std::string get_name() { return _name; }
void set_age(const int &value);
void set_gender(const char &value);
void set_name(const std::string &value);
};
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 类的封装
class Principal {
public:
Principal(int age, char gender, std::string name)
:_age(age),
_gender(gender),
_name(name)
{}
~Principal();
public:
int _age;
char _gender;
std::string _name;
// 校长
std::string _school;
//···
//···
//···
public:
const int get_age() { return _age; }
const char get_gender() { return _gender; }
const std::string get_name() { return _name; }
void set_age(const int &value);
void set_gender(const char &value);
void set_name(const std::string &value);
};
看见上边的内容,可以发现每个一共有四个类,每个类中都有相同的内容:年龄、性别、姓名、获取年龄、获取性别、获取姓名,如果以后需要添加内容,也许会有数十个甚至上百个成员函数或者成员变量是相同的。会导致代码的复用性不高,过于冗余,不够精简。因此,这个时候,我们可以用上我们的面向对象的第二个特性”继承“了。
2.3 ”继承“该怎么用?
我们依然是以”Person“类为原型来设计”学生“、”老师“、”校长“等社会角色。在这里使用了”继承“的相关内容:
#include <iostream>
#include <vector>
#include <cmath>
#include <string>
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// Person ( 基类 )
class Person {
public:
Person(int age, char gender, std::string name)
:_age(age),
_gender(gender),
_name(name)
{}
~Person();
public:
int _age;
char _gender;
std::string _name;
public:
const int get_age() { return _age; }
const char get_gender() { return _gender; }
const std::string get_name() { return _name; }
void set_age(const int &value);
void set_gender(const char &value);
void set_name(const std::string &value);
};
void Person::set_age(const int &value) {
this->_age = value;
}
void Person::set_gender(const char &value) {
this->_gender = value;
}
void Person::set_name(const std::string &value) {
this->_name = value;
}
Person::~Person()
{
//
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// Student ( 派生类,继承自Person )
class Student :public Person {
public:
Student(int age, char gender, std::string name,
double *score,std::string ID,
std::string major)
: Person(age,gender,name),
_score(score),
_studentID(ID),
_major(major)
{}
~Student(){}
private:
double *_score;
std::string _studentID;
std::string _major;
public:
const double* get_score() { return _score; }
const std::string get_id() { return _studentID; }
const std::string get_major() { return _major; }
void set_score(const double* score) { std::memcpy(_score, score, sizeof(score) / sizeof(score[0])); }
void set_id(const std::string& id) { _studentID = id; }
void set_major(const std::string& major) { _major = major; }
};
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// Teacher ( 派生类,继承自Person )
class Teacher :public Person {
public:
Teacher(int age, char gender, std::string name,
std::vector<std::string> lesson,
std::string id)
: Person(age, gender, name),
_lesson(lesson),
_teacherID(id)
{}
~Teacher() {}
private:
std::vector<std::string> _lesson;
std::string _teacherID;
public:
const std::vector<std::string> get_lesson() { return _lesson; }
const std::string get_id() { return _teacherID; }
void set_lesson(const std::vector<std::string> lesson) { _lesson.assign(lesson.begin(), lesson.end()); }
void set_id(const std::string& id) { _teacherID = id; }
};
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// Principal ( 派生类,继承自Person )
class Principal :public Person {
public:
Principal(int age, char gender, std::string name,
std::string school)
: Person(age, gender, name),
_school(school)
{}
~Principal() {}
private:
std::string _school;
public:
const std::string get_school() { return _school; }
void set_school(const std::string& school) { _school = school; }
};
int main() {
// 声明一个Person类型的对象
// 请注意:对象的概念在这里开始出现,因为Person是一个集合,一个类型,那么类型的一个一个实际的例子,就是对象
Person m_person(25, '男', "chengzhen");
// 获得年龄
m_person.get_age();
// 获得性别
m_person.get_gender();
// 获得姓名
m_person.get_name();
// 设置年龄
m_person.set_age(30);
// 设置性别
m_person.set_gender('nv');
// 设置姓名
m_person.set_name("Charmain");
std::cin.get();
}
这样看起来,不管是Student、Teacher、还是Principal类,都具备了Person的属性,同时也拥有了自己的特性,展现了三个不同人群之间的差异,但是同时,这种“is-a”的关系,也是继承的一个重要关系,即:派生类也是一个基类。并且在使用类的时候,按需使用。对于功能的扩展好,都比较明晰,减少了冗余代码,增加了可读性,这些也是“继承”能够被支持的重要原因之一。
未完,待续。