内容
- 在c++里,一个类(派生类)可以从另一个类(继承类)中继承。继承是一种复用代码的机制。每个派生类都继承其基类数据成员和成员函数,派生类只需要编写与基类行为不同或者扩展方面(继承,改写–用虚函数,新加行为方法);
- 一个派生类可以从一个基类派生(单继承),也可以从多个基类派生(多继承)。
- 派生类的定义格式:
class<派生类名>:<继承方式><基类名>{
<派生类新定义成员>
};
其中:
class是关键词
<派生类名>是新定义的一个类的名字,是从<基类名>中按照指定的<继承方式>派生出来的。
<继承方式>:
public—公有基类
private—私有基类
protected—保护基类
简单的继承实例
Base基类
#include<iostream>
using namespace std;
class Base {
private:
int m_bnumber;
public:
//构造函数生成对象
Base() {//默认构造函数
m_bnumber = 0;
cout << "Base() called "<<endl;
}
Base(int i):m_bnumber(i) //初始化列表效率高与下面的(相当于把m_bnumber赋值为i)
{//普通构造函数
//m_bnumber = i;有初始化列表就不用这个语句了
cout << "Base(i) called "<<endl;
}
//基类的析构函数,必须是virtual的,
//否则,我们再释放一个指向派生类的对象的时候
//就不会调用派生类中的析构函数了
//就只会调用基类中的析构函数了
virtual ~Base() {
cout << "~Base() called "<<endl;
}
//行为
public:
int getnumber() {
return m_bnumber;
}
virtual void print() {//后续要改写,加virtual
cout << m_bnumber << endl;
}
};
Drived派生类
class Drived :public Base {
private:
int m_dnumber;
public:
//默认构造
Drived():Base(),m_dnumber(0)//初始化列表(首先调用基类的构造函数对基类初始化,再对自己的数据成员初始化)
{
cout << "Drived() called " << endl;
}
//普通构造
Drived(int x, int y):Base(x),m_dnumber(y) {//使用初始化列表效率更高
//Base(x);
//m_dnumber = y;
cout << "Drived(int x, int y) called " << endl;
}
//析构函数
~Drived() {//若有一个新的类继承Derived类,则这个析构需要加上virtual
cout << "~Drived() called " << endl;
}
public:
//改写
void print() {
Base::print();//调用基类中的print,打印m_bnumber
cout << m_dnumber<<endl;
}
//新添加方法或者成员函数
void hello() {
cout << "hello world!" << endl;
}
};
main主函数
int main() {
Drived d(1,7);//从栈上生成Drived实例对象
//先调用基类的构造函数,再调用派生类的构造函数
cout<<d.getnumber()<<endl;//从派生类中调用基类中的getnumber//1
d.print();//1 7
d.hello();
Drived* p = new Drived(2,8);//从堆上分配了个对象
cout<<p->getnumber()<<endl;
p->print();
p->hello();
delete p;//将p释放
//p析构,先调用派生类的析构函数,再调用基类的析构函数
//可以用派生类赋值给基类
Base* p2 = new Drived(3,9);//基类指针指向派生类对象
cout<<p2->getnumber()<<endl;
p2->print();//执行的是派生类中的print();因为在基类中这个函数为虚函数,所以即使指针是基类,但是它指向的是派生类,就调用派生类
//若不加virtual则调用基类
//p2->hello();hello不是基类中的对象
delete p2;
//解释为什么基类中的析构函数为什么是virtual
//p2析构先调用指向的派生类的析构函数,再调用基类的析构函数
//不加virtual,就不会调用派生类中的析构函数,那么派生类中的某些资源可能就会泄露
//最后对象d析构
return 0;
}
结果展示
注意
- 初始化列表;
- 派生类继承了基类的数据和函数方法;
- 派生类可以自己定义新的方法和数据成员;
- 派生类中要改写(override)基类中的虚函数(带virtual关键字的函数),不能改写非虚函数;
- 基类中的 析构函数设为虚函数virtual,否则再释放对象时,可能造成派生类中资源泄露;
- 构造析构函数调用顺序:基类中的构造函数–>派生类中的构造函数–>派生类中的析构函数–>基类中的析构函数;
DEMO
基类-人:构造 ,析构,
eat(),getname(),ishealthy();
派生类-男人:构造,析构,eat(),smoke()
派生类-女人:构造,析构,eat(),buy()
基类person
#include <iostream>
using namespace std;
class Person {
public://构造函数
Person() { //默认构造函数
m_name = new char[1];
*m_name = '\0';
m_age = 0;
m_sex = 'M';
m_height = 0.0f;
m_weight = 0.0f;
cout << "Person() called " <<m_name << endl;
};
Person(const char* name, int age, char sex, float height, float weight);//普通构造函数
virtual ~Person(){//析构函数加virtual
cout << "~person called "<<m_name<<endl;
}
public://工具函数
char* getname() {
return m_name;
}
void setname(char *name) {
if (name == NULL) {
m_name = new char[1];
*m_name = '\0';
}
else {
m_name = new char[strlen(name)+1];
strcpy_s(m_name, strlen(name) + 1,name);
}
}
private://数据成员
char *m_name;
int m_age;
char m_sex;
float m_height;
float m_weight;
public://行为
virtual void eat() {//加virtual
cout <<m_name<< " is eating!!!" << endl;
}
bool ishealth() {
float bmi = m_weight / (m_height*m_height);
return bmi <= 25.0f && bmi >= 20.0f;
}
};
Person::Person(const char* name, int age, char sex, float height, float weight) {
m_name = new char[strlen(name)+1];
strcpy_s(m_name, strlen(name) + 1,name);
m_age = age;
m_sex = sex;
m_height = height;
m_weight = weight;
cout << "Person(char* name, int age, char sex, float height, float weight) called " << m_name << endl;
}
man派生类
class man :public Person {
private:
int m_house;//房数
public:
man() :Person() {//默认构造
cout << "man() :Person() called " << endl;
}
man(const char* name, int age, float height, float weight, int num) :Person(name,
age, 'M', height, weight), m_house(num) {//普通股构造函数
cout << "man(const char* name, int age, float height, float weight,int num) called" << endl;
}
~man() {//析构函数
cout << "~man() called " << endl;
}
public:
//行为
void eat() {
Person::eat();
cout << "eating pork(吃肉)"<<Person::getname()<<endl;
}
//新加
void smoking()
{
cout << "smoking " << Person::getname()<< endl;
}
};
woman派生类
class woman :public Person {
private:
int m_clothes;//衣服数
public:
woman() :Person() {//默认构造
cout << "woman() :Person() called " << endl;
}
woman(const char* name, int age, float height, float weight, int num) :Person(name,
age, 'F', height, weight), m_clothes(num) {//普通股构造函数
cout << "woman(const char* name, int age, float height, float weight,int num) called" << endl;
}
~woman() {//析构函数
cout << "~woman() called " << endl;
}
public:
//行为
void eat() {
Person::eat();
cout << "eating vegetables(吃蔬菜)"<<Person::getname() << endl;
}
//新加
void buy()
{
cout << "buying " << Person::getname() << endl;
}
};
main主函数
int main()
{
man tom("tom",27,1.83f,72.1f,2);
woman lily("lily",25,1.71f,61.2f,100);
if (tom.ishealth()) {
tom.eat();
tom.smoking();
}
else {
cout << "healthy warning " << tom.getname()<<endl;
}
if (lily.ishealth()) {
lily.eat();
lily.buy();
}
else {
cout << "healthy warning " <<lily.getname()<< endl;
}
Person* p = new man("jerry",21,1.78f,72.1f,3);
if (p->ishealth()) {
p->eat();//调用的是派生类中的,多态
//基类中无smoking
delete p;//释放p指向的内存之前,会调用派生类的析构函数
//和基类的析构函数,前提是基类的析构函数前有virtual
}
注意
- delete p; //释放p指向的内存之前,会调用派生类的析构函数
//和基类的析构函数,前提是基类的析构函数前有virtual