在面向对象编程中,继承和派生是两个重要的概念,它们允许程序员从已有的类创建新的类。这些概念在不同的编程语言中有不同的实现方式,但基本的思路是相似的。
1.继承 (Inheritance): 继承是从已有的类创建一个新类。新类(子类)继承了父类(基类)的所有属性和方法。子类可以重写父类的某些方法,或者添加新的属性和方法。继承是面向对象编程中的一个基本特性,它允许代码的重用和模块化。
例如,如果我们有一个“动物”类,我们可以从这个类派生出“狗”类和“猫”类。在这种情况下,“狗”类和“猫”类继承了“动物”类的所有属性和方法。
- 派生 (Derivation): 在某些语境中,“派生”和“继承”是同义词。然而,在其他语境中,“派生”可能指的是一个更具体的概念,即从一个基类派生出一个或多个子类。在这种情况下,“派生”与“继承”略有不同,因为它更强调从一个基类创建多个子类的过程。
总的来说,无论使用“继承”还是“派生”,都是为了创建更具体的类,这些类继承并扩展了更一般的类的属性和方法。在实现这些概念时,需要考虑到代码的重用、封装和多态性等面向对象编程的基本原则。
![第十二章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人](https://www.xiciw.com/wp-content/uploads/2024/01/2024012819012084.webp)
上一章我们讲了运算符重载(Operator Overloading)下面继续讲解继承与派生(Inherit&&Derive)
7. 继承与派生(Inherit&&Derive)
7.1. 引入
在 C++中可重用性(software reusability)是通过继承(inheritance)这一机制来实现的。如果没有掌握继承性,就没有掌握类与对象的精华。
引例
#include <iostream>
using namespace std;
class Student
{
public:
void dis()
{
cout<<name<<endl;
cout<<age<<endl;
}
string name;
int age;
};
//重写:
//class Student2
//{
//public:
// void dis()
// {
// cout<<name<<endl;
// cout<<age<<endl;
// cout<<sex<<endl;
// cout<<score<<endl;
// }
//private:
// string name;
// int age;
// char sex;
// float score;
//};
//继承:
class Student2:public Student
{
public:
Student2(string n,int a,char s,float f)
{
name = n;
age = a;
sex = s;
score = f;
}
void dis()
{
Student::dis();
cout<<sex<<endl;
cout<<score<<endl;
}
char sex;
float score;
};
int main()
{
Student2 s("wangwu",23,'s',200);
s.dis();
return 0;
}
7.2. 定义
类的继承,是新的类从已有类那里得到已有的特性。或从已有类产生新类的过程就是类的派生。原有的类称为基类或父类,产生的新类称为派生类或子类。
派生与继承,是同一种意义两种称谓。
isA 的关系。
7.3. 继承
7.3.1. 语法
派生类的声明:
class 派生类名:[继承方式] 基类名
{
派生类成员声明;
};
一个派生类可以同时有多个基类,这种情况称为多重继承,派生类只有一个基类,称为单继承。下面从单继承讲起。
7.3.2. 继承方式
继承方式规定了如何访问基类继承的成员。继承方式有 public, private, protected。继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。
简单讲:
公有继承:基类的公有成员和保护成员在派生类中保持原有访问属性,其私有成员仍为基类的私有成员。
私有继承:基类的公有成员和保护成员在派生类中成了私有成员,其私有成 员仍为基类的私有成员。
保护继承:基类的公有成员和保护成员在派生类中成了保护成员,其私有成 员仍为基类的私有成员。
pretected 对于外界访问属性来说,等同于私有,但可以派生类中可见。
#include <iostream>
using namespace std;
class Base
{
public:
int pub;
protected:
int pro;
private:
int pri;
};
class Drive:public Base
{
public:
void func()
{
pub = 10;
pro = 100;
// pri = 1000;
}
};
//
int main()
{
Base b;
b.pub = 10;
// b.pro = 100;
// b.pri = 1000;
return 0;
}
7.3.3. 派生类的组成
派生类中的成员,包含两大部分,一类是从基类继承过来的,一类是自己增加的成员。从基类继承过过来的表现其共性,而新增的成员体现了其个性。
![第十二章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人](https://www.xiciw.com/wp-content/uploads/2024/01/2024013012455878.webp)
几点说明:
1,全盘接收,除了构造器与析构器。基类有可能会造成派生类的成员冗余,所以说基类是需设计的。
2,派生类有了自己的个性,使派生类有了意义。
7.4. 派生类的构造
派生类中由基类继承而来的成员的初始化工作还是由基类的构造函数完成,然后派生
类中新增的成员在派生类的构造函数中初始化。
7.4.1. 派生类构造函数的语法:
派生类名::派生类名(参数总表)
:基类名(参数表),内嵌子对象(参数表)
{
派生类新增成员的初始化语句; //也可出现地参数列表中
}
注:
构造函数的初始化顺序并不以上面的顺序进行,而是根据声明的顺序初始化。
如果基类中没有默认构造函数(无参),那么在派生类的构造函数中必须显示调用基
类构造函数,以初始化基类成员。
派生类构造函数执行的次序:
基类-->成员-->子类
a 调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左到右);
b 调用内嵌成员对象的构造函数,调用顺序按照它们在类中声明的顺序;
c 派生类的构造函数体中的内容。
7.4.2. 代码实现
祖父类
student.h
class Student
{
public:
Student(string sn,int n,char s);
~Student();
void dis();
private:
string name;
int num;
char sex;
};
student.cpp
Student::Student(string sn, int n, char s)
:name(sn),num(n),sex(s)
{
}
Student::~Student()
{
}
void Student:: dis()
{
cout<<name<<endl;
cout<<num<<endl;
cout<<sex<<endl;
}
父类
graduate.h
class Graduate:public Student
{
public:
Graduate(string sn,int in,char cs,float fs);
~Graduate();
void dump()
{
dis();
cout<<salary<<endl;
}
private:
float salary;
};
graduate.cpp
Graduate::Graduate(string sn, int in, char cs, float fs)
:Student(sn,in,cs),salary(fs)
{
}
Graduate::~Graduate()
{
}
类成员
birthday.h
class Birthday
{
public:
Birthday(int y,int m,int d);
~Birthday();
void print();
private:
int year;
int month;
int day;
};
birthday.cpp
Birthday::Birthday(int y, int m, int d)
:year(y),month(m),day(d)
{
}
Birthday::~Birthday()
{
}
void Birthday::print()
{
cout<<year<<month<<day<<endl;
}
子类
doctor.h
class Doctor:public Graduate
{
public:
Doctor(string sn,int in,char cs,float fs,string st,int iy,int im,in
t id);
~Doctor();
void disdump();
private:
string title;
Birthday birth; //类中声明的类对象
};
doctor.cpp
Doctor::Doctor(string sn, int in, char cs, float fs, string st, int iy,
int im, int id)
:Graduate(sn,in,cs,fs),birth(iy,im,id),title(st)
{
}
Doctor::~Doctor()
{
}
void Doctor::disdump()
{
dump();
cout<<title<<endl;
birth.print();
}
测试代码
int main()
{
Student s("zhaosi",2001,'m');
s.dis();
cout<<"----------------"<<endl;
Graduate g("liuneng",2001,'x',2000);
g.dump();
cout<<"----------------"<<endl;
Doctor d("qiuxiang",2001,'y',3000,"doctor",2001,8,16);
d.disdump();
return 0;
}
7.4.3. 结论
子类构造器中,要么显示的调用父类的构造器(传参),要么隐式的调用。发生隐式调用时,父类要有无参构造器或是可以包含无参构造器的默认参数函数。子类对象亦然
7.5. 派生类的拷贝构造
7.5.1. 格式
派生类::派生类(const 派生类& another)
:基类(another),派生类新成员(another.新成员)
{
}
7.5.2. 代码
父类
student.h
class Student
{
public:
Student(string sn,int n,char s);
Student(const Student & another);
~Student();
void dis();
private:
string name;
int num;
char sex;
};
student.cpp
Student::Student(string sn, int n, char s)
:name(sn),num(n),sex(s)
{
}
Student::~Student()
{
}
void Student:: dis()
{
cout<<name<<endl;
cout<<num<<endl;
cout<<sex<<endl;
}
Student::Student(const Student & another)
{
name = another.name;
num = another.num;
sex = another.sex;
}
子类
graduate.h
class Graduate:public Student
{
public:
Graduate(string sn,int in,char cs,float fs);
~Graduate();
Graduate(const Graduate & another);
void dump()
{
dis();
cout<<salary<<endl;
}
private:
float salary;
};
graduate.cpp
Graduate::Graduate(string sn, int in, char cs, float fs)
:Student(sn,in,cs),salary(fs)
{
}
Graduate::~Graduate()
{
}
Graduate::Graduate(const Graduate & another)
:Student(another),salary(another.salary)
{
}
测试代码
int main()
{
Graduate g("liuneng",2001,'x',2000);
g.dump();
Graduate gg = g;
gg.dump();
return 0;
}
7.5.3. 结论:
派生类中的默认拷贝构造器会调用父类中默认或自实现拷贝构造器,若派生类中自实现拷贝构造器,则必须显示的调用父类的拷贝构造器。