一、關於基類與派生類之間賦值、初始化以及指針指向
/*公有繼承: 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.使用基類的引用或者基類的指針來接收基類的對象和派生類的對象,不用銷燬臨時對象,效率更高。