C++继承4 Is a 基类的指针指向派生类的对象
一、关于基类与派生类之间赋值、初始化以及指针指向
/*公有继承: Is a
1.Person类,数据成员:m_strName 成员函数:构造、析构、piay()
2.Soldier类,数据成员:m_iAge 成员函数:构造、析构、work()
*/
代码:
Person.h
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(string name = "v");//初始化列表
~Person();
void play();
protected:
string m_strName;
};
Person.cpp
#include"Person.h"
Person::Person(string name)
{
m_strName = name;
cout<<"Person()"<<endl;
}
Person::~Person()
{
cout<<"~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 = "wls",int age = 20);//初始化列表
~Soldier();
void work();
protected:
int m_iAge;
};
Soldier.cpp
#include"Soldier.h"
Soldier::Soldier(string name,int age)
{
m_strName = name;
m_iAge = age;
cout<<"Soldier"<<endl;
}
Soldier::~Soldier()
{
cout<<"~Soldier"<<endl;
}
void Soldier::work()
{
cout<<m_strName<<endl;
cout<<m_iAge<<endl;
cout<<"Soldier--work()"<<endl;
}
1、基类的指针指向派生类的对象
main.cpp
#include"Soldier.h"
int main(void)
{
Soldier soldier;
Person *p = &soldier;//指针指向,用父类的指针指向子类的对象
p->play();
//p->work();//erro 用父类的指针只能调用自己的数据成员和成员函数,而不能调用子类的
return 0;
}
运行结果:
用父类的指针只能调用自己的数据成员和成员函数
2、用派生类的对象来初始化基类的对象
main.cpp
#include"Soldier.h"
int main(void)
{
Soldier soldier;
Person p = soldier;//用soldier这个对象来初始化person的p
p.play();//p中的m_strName的值-->soldier中的wls
return 0;
}
运行结果:
用soldier这个对象来初始化person的p,执行结果先构造person()再构造soldier(),用p来调用自己的成员函数play(),p中m_strName的值为soldier中的wls;说明用soldier这个对象来初始化person的p这个方式,可以使soldier当中的m_strName赋值给父类对象当中的对应的数据成员。
3、将派生类对象赋值给基类对象
main.cpp
#include"Soldier.h"
int main(void)
{
Soldier soldier;
Person p;
p = soldier;//让soldier直接赋值给p这个对象
p.play();//打印wls
return 0;
}
运行结果:
综上:无论是用soldier去初始化p这个对象还是将soldier直接赋值给p这个对象还是用指针指向的方式,soldier当中的m_strName都能赋值给父类对象当中的对应的数据成员。
二、用父类的指针指向堆中子类的对象,销毁的时候执行了父类的析构函数而没有执行子类的,这样会造成内存泄漏-->如何避免-->virtual
main.cpp
int main(void)
{
Person *p = new Soldier;
p->play();
delete p;
p = NULL;
}
运行结果:
我们发现在delete的时候,执行了父类的析构函数而没有执行子类的,这样会造成内存泄漏,那么该如何避免呢?—>使用virtual关键字
在Person.h和Soldier.h中的析构函数前加上virtual关键字,再次运行
virtual关键字是可以被继承下去的
运行结果:
三、定义函数test1(Person p) test2(Person &p) test3(Person *p)
/*公有继承: Is a
1.Person类,数据成员:m_strName 成员函数:构造、析构、piay()
2.Soldier类,数据成员:m_iAge 成员函数:构造、析构、work()
3.定义函数test1(Person p) test2(Person &p) test3(Person *p)
*/
1、参数是基类的对象
main.cpp
#include"Soldier.h"
void test1(Person p)//参数是对象p,传值的时候先实例化一个临时对象p,所以执行完后要销毁
{
p.play();
}
int main(void)
{
Person p;
Soldier s;
test1(p);//如果函数的参数是基类的对象,那么基类的对象和派生类的对象都可以作为实参传递进来并且能都正常使用
test1(s);
return 0;
}
运行结果:
如果函数的参数是基类的对象,那么基类的对象和派生类的对象都可以作为实参传递进来并且能都正常使用,只不过参数是对象,传值的时候要先实例化一个临时对象p,所以执行完后要销毁。
2、参数是基类的引用
main.cpp
#include"Soldier.h"
void test2(Person &p)//使用基类的引用也可以接收基类的对象和派生类的对象
{
p.play();
}
int main(void)
{
Person p;
Soldier s;
test2(p);
test2(s);
return 0;
}
运行结果:
引用 在传入参数的时候,会将这个参数起一个别名p,通过这个别名p来调用play;在这个过程当中并没有实例化一个临时对象,所以也没有销毁临时对象的痕迹。
使用基类的引用也可以接收基类的对象和派生类的对象
3、参数是基类的指针
main.cpp
#include"Soldier.h"
void test3(Person *p)//参数是一个基类的指针
{
p.play();
}
int main(void)
{
Person p;
Soldier s;
test3(&p);
test3(&s);
return 0;
}
运行结果:
使用基类的指针也可以接收基类的对象和派生类的对象
综上:
1.使用基类的对象、引用、指针都可以接收基类的对象和派生类的对象;
2.使用基类的引用或者基类的指针来接收基类的对象和派生类的对象,不用销毁临时对象,效率更高。