教程:https://www.imooc.com/learn/426
继承
举例:
上例优化:
关系:
对应使用,不能交叉
编码示例:
Person.h
#include <string>
using namespace std;
class Person
{
public:
Person();
~Person();
void eat();
public:
string m_strName;
int m_iAge;
};
Person.cpp
#include <iostream>
#include "Person.h"
using namespace std;
Person::Person()
{
cout << "Person()" << endl;
}
Person::~Person()
{
cout << "~Person()" << endl;
}
void Person::eat()
{
cout << "eat()" << endl;
}
Worker.h
#include "Person.h"
class Worker : public Person
{
public:
Worker();
~Worker();
void work();
public:
int m_iSalary;
};
Worker.cpp
#include <iostream>
#include "Worker.h"
using namespace std;
Worker::Worker()
{
cout << "Worker()" << endl;
}
Worker::~Worker()
{
cout << "~Worker()" << endl;
}
void Worker::work()
{
cout << "work()" << endl;
}
demo.cpp
include <iostream>
#include "Worker.h"
using namespace std;
int main()
{
Worker *p = new Worker(); //从堆中实例化对象
p->m_strName = "zhang"; //检验是否能使用父类的数据成员及成员函数
p->m_iAge = 10;
p->eat();
p->m_iSalary = 100; //检验是否能使用自身的数据成员及成员函数
p->work();
delete p;
p = NULL;
/*
Worker worker; //从栈中实例化对象
worker.m_strName = "zhang"; //检验是否能使用父类的数据成员及成员函数
worker.m_iAge = 10;
worker.eat();
worker.m_iSalary = 100; //检验是否能使用自身的数据成员及成员函数
worker.work();
//结合上例可知,无论是从堆中还是从栈中实例化对象,在公共继承中,父类的public访问限定符下的数据成员和成员函数都被继承到了子类的public访问限定符下
*/
return 0;
}
由运行结果知,要实例化一个子类(派生类),要先实例化父类(基类)。销毁时逆序
继承方式
公有继承:
public访问限定符下:
在使用时,Worker实例化的对象可以调用Person的数据成员和成员函数
protected访问限定符下:
实例化的对象可以调用类中在public下的eat(),但不能访问在protected下的m_iAge和在private下的m_strName
在实现成员函数时,可以访问在protected下的m_iAge和在private下的m_strName
在公有继承中:
private访问限定符下:
父类的private下的被子类继承到不可见位置,不是子类的private下,所以不可访问m_iAge
private成员会继承到派生类中,但无法通过派生类直接访问
举例证明:
Person.h
#include <string>
using namespace std;
class Person
{
public:
Person();
~Person();
void eat();
protected:
string m_strName;
int born;
private:
int m_iAge;
string tel;
};
Person.cpp
#include <iostream>
#include "Person.h"
using namespace std;
Person::Person()
{
cout << "Person()" << endl;
}
Person::~Person()
{
cout << "~Person()" << endl;
}
void Person::eat()
{
m_strName = "zhang";
m_iAge = 20;
cout << "eat()" << endl;
}
Worker.h
#include "Person.h"
class Worker : public Person
{
public:
Worker();
~Worker();
void work();
void mas();
public:
int m_iSalary;
};
Worker.cpp
#include <iostream>
#include "Worker.h"
using namespace std;
Worker::Worker()
{
cout << "Worker()" << endl;
}
Worker::~Worker()
{
cout << "~Worker()" << endl;
}
void Worker::work()
{
m_strName = "wang";
born = 2000;
cout << "work()" << endl;
}
void Worker::mas()
{
m_iAge = 10;
tel = "12345";
cout << "mas()" << endl;
}
demo.cpp
#include <iostream>
#include "Worker.h"
#include "Person.h"
using namespace std;
int main()
{
Person person;
person.eat();
//程序能正常运行,说明在protected和private访问限定符下定义的数据成员,在其成员函数中是可以访问的
//person.m_strName = "li"; //该句会报错,说明通过外部对象无法访问。m_iAge也一样
Worker worker;
worker.work(); //能正常运行,说明Person中protected的数据成员被继承到Worker中的protected下,可访问
//worker.mas(); //不能运行,//通过公有继承方式,父类的private下的数据成员继承到子类中,所以也就无法访问
return 0;
}
保护继承
private成员会继承到派生类中,但无法通过派生类直接访问
私有继承
private成员会继承到派生类中,但无法通过派生类直接访问
包含关系,父类包含在子类中
编码示例:
公有继承复习:
Person.h
#include <string>
using namespace std;
class Person
{
public:
Person();
void play();
protected:
string m_strName;
};
Person.cpp
#include <iostream>
#include "Person.h"
using namespace std;
Person::Person()
{
m_strName = "zhang";
}
void Person::play ()
{
cout << "Person::play ()" << endl;
cout << m_strName << endl;
}
Soldier.h
#include "Person.h"
class Soldier :public Person
{
public:
Soldier();
void work();
protected:
int m_iAge;
};
Soldier.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
Soldier::Soldier()
{
}
void Soldier::work()
{
m_strName = "wang"; //Person类的protected成员
m_iAge = 20; //Soldier类的protected成员
cout << m_strName << endl;
cout << m_iAge << endl;
cout << "Soldier::work()" << endl;
}
Infantry.h
//步兵类
#include "Soldier.h"
class Infantry :public Soldier
{
public :
void attack();
};
Infantry.cpp
#include <iostream>
#include "Infantry.h"
using namespace std;
void Infantry::attack()
{
cout << "Infantry::attack()" << endl;
}
demo.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
int main()
{
Soldier soldier;
soldier.work(); //调用Soldier类(相对Person类来说是子类)的成员函数,间接调用了父类的protected成员m_strName
soldier.play(); //调用父类的public成员函数
return 0;
}
能正常运行
保护继承:
修改:
Soldier.h
#include "Person.h"
class Soldier :protected Person //保护继承,父类的public和protected成员都会继承到子类的protected下
{ //通过子类的对象,只能访问到子类的public下的数据和函数,不能访问到父类public下的数据和函数
public:
Soldier();
void work();
protected:
int m_iAge;
};
Infantry.cpp
#include <iostream>
#include "Infantry.h"
using namespace std;
void Infantry::attack()
{
m_strName = "li"; //访问Person类的protected数据成员(被继承到Soldier类的protected下)
cout << m_strName << endl;
cout << "Infantry::attack()" << endl;
}
demo.cpp
#include <iostream>
#include "Soldier.h"
#include "Infantry.h"
using namespace std;
int main()
{
Soldier soldier;
soldier.work(); //调用Soldier类(相对Person类来说是子类)的成员函数,间接调用了父类的protected成员m_strName
//soldier.play(); //调用父类的public成员函数,此时此句报错,因为现在是protected继承,不能访问父类public的成员函数
Infantry infantry;
infantry.attack();
return 0;
}
infantry.attack();
能正常运行,说明Soldier类保护继承Person类,Person类的public和protected成员被继承到Soldier类的protected下,Infantry类公有继承Soldier类(可以访问Soldier类的public和protected成员),所以Infantry类的attack()函数中可以访问Person类的protected成员m_strName
私有继承:
修改:
Soldier.h
#include "Person.h"
class Soldier :private Person //私有继承,父类的public和protected成员都会继承到子类的private下
{ //理论上,可以在Soldier中直接访问Person的相关数据,但Infantry类公有继承Soldier类后,Infantry类无法直接访问Person类的数据成员和成员函数
public:
Soldier();
void work();
protected:
int m_iAge;
private:
};
Infantry.cpp
#include <iostream>
#include "Infantry.h"
using namespace std;
void Infantry::attack()
{
m_strName = "li"; //运行demo,出错
cout << m_strName << endl;
cout << "Infantry::attack()" << endl;
}
demo.cpp
#include <iostream>
#include "Soldier.h"
#include "Infantry.h"
using namespace std;
int main()
{
Infantry infantry;
infantry.attack();
return 0;
}
B类从A类公共派生,那么A类的私有成员函数不能被B类继承并使用。(√)
B类从A类公共派生,那么A类的私有成员函数成为B类的私有成员函数。(×)
隐藏
B公有继承A,此时B中自己有函数ABC(),又从A中继承到了函数ABC(),A、B中都有函数ABC(),当B的对象直接调用ABC()时,调用到的是B的ABC()无法访问到A的ABC()。这样子叫做同名隐藏。同名隐藏不仅局限于函数,数据成员也有,但少见。
第一个调用的是Soldier的play()
第二个调用的是Person的play()
编码示例:
隐藏的使用:
Person.h
#include <string>
using namespace std;
class Person
{
public:
Person();
void play();
protected:
string m_strName;
};
Person.cpp
#include <iostream>
#include "Person.h" //" " 搜索的是同目录下的头文件,< > 搜索的是系统的默认库
using namespace std;
Person::Person()
{
m_strName = "zhang";
}
void Person::play ()
{
cout << "Person::play ()" << endl;
cout << m_strName << endl;
}
Soldier.h
#include "Person.h"
class Soldier :public Person
{
public:
Soldier();
void play();
void work();
protected:
private:
};
Soldier.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
Soldier::Soldier()
{
}
void Soldier::play()
{
cout << "Soldier::play()" << endl;
}
void Soldier::work()
{
cout << "Soldier::work()" << endl;
}
demo.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
int main()
{
Soldier soldier;
soldier.play();
soldier.work();
soldier.Person::play();
return 0;
}
当同名函数的参数不同时:
修改:
soldier.h
#include "Person.h"
class Soldier :public Person
{
public:
Soldier();
void play(int x);
void work();
protected:
private:
};
Soldier.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
Soldier::Soldier()
{
}
void Soldier::play(int x)
{
cout << "Soldier::play()" << endl;
}
void Soldier::work()
{
cout << "Soldier::work()" << endl;
}
demo.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
int main()
{
Soldier soldier;
soldier.play(7);
soldier.work();
//soldier.play(); //此句错误,不能通过这个调用Person类的play(),系统只会提示不符合Soldier类的play(int x)的格式,缺少参数
return 0;
}
所以就算同名函数的参数不同,也不能直接调用,父类的还是会被隐藏
修改:同名数据成员
Soldier.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
Soldier::Soldier()
{
}
void Soldier::play(int x)
{
cout << "Soldier::play()" << endl;
cout << m_strName << endl; //打印出来的是在Soldier类中定义的m_strName
cout << Person::m_strName << endl; //Person类定义的m_strName
}
void Soldier::work()
{
m_strName = "Jack"; //给赋值的是Soldier类中定义的m_strName,不是Person类中定义的m_strName
Person::m_strName = "Jackson"; //Person类定义的m_strName
cout << "Soldier::work()" << endl;
}
demo.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
int main()
{
Soldier soldier;
soldier.work();
soldier.play(7);
return 0;
}
isA
Person p1 = s1;
//用s1实例化p1
派生类的对象可以赋值给基类,可以用基类的指针指向派生类的对象
可以将基类的指针或基类的对象或基类的引用作为函数的参数,使函数可以接收传入的派生类的对象,并且也可以传入基类的对象
存储结构:
1、将子类的对象赋值给父类的对象(用子类的对象初始化父类的变量)
赋值时,父类没有的数据成员会丢失
2、父类的指针指向一个子类对象
父类的指针只能访问到父类原有的数据成员,无法访问到其他子类独有的数据成员
编码示例:
Person.h
#include <string>
using namespace std;
class Person
{
public:
Person(string name = "Jim");
~Person();
void play();
protected:
string m_strName;
};
Person.cpp
#include <iostream>
#include "Person.h"
using namespace std;
Person::Person(string name)
{
m_strName = name;
cout << "Person::Person(string name)" << endl;
}
Person::~Person()
{
cout << "Person::~Person()" << endl;
}
void Person::play ()
{
cout << "Person::play ()" << endl;
cout << m_strName << endl;
}
Soldier.h
#include "Person.h"
class Soldier :public Person
{
public:
Soldier(string name = "Jack", int age = 20);
~Soldier();
void work();
protected:
int m_iAge;
private:
};
Soldier.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
Soldier::Soldier(string name, int age)
{
m_strName = name;
m_iAge = age;
cout << "Soldier::Soldier(string name, int age)" << endl;
}
Soldier::~Soldier()
{
cout << "Soldier::~Soldier()" << endl;
}
void Soldier::work()
{
cout << m_strName << endl;
cout << m_iAge << endl;
cout << "Soldier::work()" << endl;
}
demo.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
int main()
{
Soldier soldier;
Person p = soldier; //或: Person p; p = soldier; 第一个是用soldier初始化Person的对象,第二个是将soldier赋值给Person的对象。两个取得的结果一样
p.play();
return 0;
}
用Soldier实例化Person的对象时,Soldier本身是要执行构造函数的,因为Soldier是子类,所以执行Soldier的构造函数前,会先执行父类Person的构造函数。接下来调用的函数是Person 类的,原来在Person初始化的m_strName = “Jim”,在Soldier类初始化的m_strName = “Jack”,由结果可知,用Soldier初始化Person类后,Soldier中的m_strName的值赋给了Person中的m_strName
修改demo.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
int main()
{
Soldier soldier;
Person p;
p.play();
return 0;
}
可对比结果
修改(改用指针):
demo.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
int main()
{
Soldier soldier;
Person *p = &soldier;
p->play();
//p->work(); //报错,说明使用Person的对象或指针,只能调用Person自有的数据成员和成员函数,无法调用子类的数据成员和成员函数
return 0;
}
所以,无论是用对象赋值的方式,还是用指针指向的方式,如果用父类接收子类对象的值或指向子类对象,打印出来的都是子类对象的值
修改:
demp.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
int main()
{
Person *p = new Soldier;
p->play();
delete p;
p = NULL;
return 0;
}
用父类的指针指向子类的对象时,子类的对象会进行实例化,实例化的过程会先调用父类的构造函数,再调用子类的构造函数。当delete销毁时,只执行父类了的析构函数,这样子类没有析构,可能造成内存泄露,要修改,用虚析构函数。
虚析构函数:当存在继承关系时,使用父类的指针指向堆中的子类的对象(用new实例化,用->调用的),还想使用父类的指针去释放内存,这时就需要虚析构函数
virtual ~Person(); //虚析构函数,virtual是可以被继承的,所以即便在Soldier类的析构函数不写virtual,该析构函数也是虚析构函数
修改:
Person.h
#include <string>
using namespace std;
class Person
{
public:
Person(string name = "Jim");
virtual ~Person();
void play();
protected:
string m_strName;
};
Soldier.h
#include "Person.h"
class Soldier :public Person
{
public:
Soldier(string name = "Jack", int age = 20);
virtual ~Soldier();
void work();
protected:
int m_iAge;
private:
};
可见虽然demo.cpp中并没有delete Soldier类的对象,但是Soldier类的析构函数还是被调用了
编码举例2(体现函数参数传递关系)
demo.cpp
#include <iostream>
#include "Soldier.h"
using namespace std;
void test1(Person p)
{
p.play();
}
void test2(Person &p)
{
p.play();
}
void test3(Person *p)
{
p->play();
}
int main()
{
Person p;
Soldier s;
test1(p); //传入的是Person的对象,输出结果是Jim
test1(s); //传入的是Soldier的对象,输出结果是Jack
//可见,如果函数参数是基类的对象,基类的对象和派生类的对象都可以作为实参传递进来,并能够正常使用
return 0;
}
结果可见,先调用了Person的构造函数两次,再调用Soldier的构造函数,这是因为先实例化一个Person的对象,调用一次Person的构造函数,然后实例化一个Soldier的对象(Soldier继承Person),所以先后调用一次Person的构造函数和Soldier的构造函数。
test1定义的参数是一个对象,所以传值时会先实例化一个临时对象p,通过临时对象p调用play(),在test1这个函数执行完毕时,临时对象p会自动销毁,这就是为什么在两次调用play()时,都会分别执行一次Person的析构函数。
修改(把两个test1改为test2)
实例化Person对象(第一行),实例化Soldier对象(2、3行),调用test2,是引用,所以在传入参数时,会给参数起别名p,通过别名p调用play(),在test2执行过程中,没有实例化临时对象,所以没有销毁临时对象,其他输出结果与test1输出结果一致。说明使用基类的引用也可以接收基类的对象和派生类的对象
修改为:
test3(&p);
test3(&s);
输出结果与test2一致,因为test3的参数是基类的指针,当使用基类或派生类的对象地址传入后,会使用指针p分别调用基类或派生类的play()
对比test1、test2、test3,使用2、3不会产生新的临时对象,效率更高。