1.派生類如何構造:
對於簡單的派生類,即只有一個基類,且直接派生(多繼承將在後續幾節中做詳細講解),來講,如果基類的構造函數沒有參數,或者沒有顯式定義構造函數,那麼派生類可以不向基類傳遞參數,甚至可以不定義構造函數。但是一旦基類含有帶參數的構造函數時,派生類必須定義構造函數,並把參數傳遞給基類構造函數。其一般格式:
派生類名(參數總表)::基類名(參數子表)
{
派生類新增成員的初始化語句;
}
而析構函數可以有用戶自己定義,由於其是不帶參數的,所以在派生類中是否要自定義析構函數與它所屬基類的析構函數無關。在執行派生類的構造函數時,系統會自動調用基類的析構函數,進行對象清理。下面舉例來說明一下簡單派生類的構造和析構:
#include "stdafx.h"
#include <iostream>
#include <string>
class C
{
private:
std::string c;
public:
C(){};
C(std::string c);
~C();
void showC();
};
C::C(std::string c)
{
this->c=c;
std::cout<<"構造對象c"<<std::endl;
}
C::~C()
{
std::cout<<"清理對象c"<<std::endl;
}
void C::showC()
{
std::cout<<"c="<<this->c<<std::endl;
}
class D:private C
{
private:
std::string d;
public:
D(std::string d,std::string c);
~D();
void showD();
};
D::D(std::string d,std::string c):C(c) //參數傳遞給基類,也可以不寫C(c),那麼就調用了C類中無參構造函數
{
this->d=d;
std::cout<<"構造對象d"<<std::endl;
}
D::~D()
{
std::cout<<"清理對象d"<<std::endl;
}
void D::showD()
{
showC();
std::cout<<"d="<<this->d<<std::endl;
}
int main()
{
{
D d("ddd","ccc");
d.showD();
}
return0;
}
結果:
在結果裏我們可以看到派生類構造函數和析構函數的調用順序,當創建派生類對象時,首先調用基類的構造函數,再調用派生類構造函數,而當清理對象時,則剛好相反。
那麼如果當派生類中存在成員對象,那麼構造函數和析構函數的調用順序又如何呢?在解答這個問題之前,我們先看一下當派生類中含有子對象時,構造函數的一般格式:
派生類名(參數總表):基類名(參數子表),對象名1(參數子表1),對象名2(參數子表2)
{
派生類新增成員的初始化語句;
}
還是,我們用一個示例來說明,並通過實際的運行結果來解答上述的問題:
#include "stdafx.h"
#include <iostream>
#include <string>
class C
{
private:
std::string c;
public:
C(){};
C(std::string c);
~C();
void showC();
};
C::C(std::string c)
{
this->c=c;
std::cout<<"構造c值爲"<<c<<"的對象c"<<std::endl;
}
C::~C()
{
std::cout<<"清理c值爲"<<c<<"的對象c"<<std::endl;
}
void C::showC()
{
std::cout<<"c="<<this->c<<std::endl;
}
class D:private C
{
private:
std::string d;
C c;
public:
D(std::string d,std::string c1,std::string c2);
~D();
void showD();
};
D::D(std::string d,std::string c1,std::string c2):c(c2),C(c1) //參數傳遞給基類,也可以不寫C(c1)或c(c2),那麼就調用了C類中無參構造函數
{
this->d=d;
std::cout<<"構造對象d"<<std::endl;
}
D::~D()
{
std::cout<<"清理對象d"<<std::endl;
}
void D::showD()
{
showC();
c.showC();
std::cout<<"d="<<this->d<<std::endl;
}
int main()
{
{
D d("ddd","ccc1","ccc2");
d.showD();
}
return0;
}//結果:
從結果中很清楚的說明了其構造函數和析構函數的調用順序。另外讀者也可以在類D成員對象c後面再聲明一個基類對象,運行看看,修改過後其構造和析構的調用順序又如何?最後一點要補充的是,如果派生類的基類也是派生類,那麼每個派生類只需要負責其直接的基類數據成員的初始化。
2.在定義派生類時,C++裏是允許派生類中的成員名和基類中的成員名相同,出現這種情況,我們稱派生類成員覆蓋了基類中使用相同名稱的成員。也就是說當你在派生類中或用對象訪問該同名成員時,你所訪問只是派生類中的成員,基類中的就自動被忽略。但如果我確實要訪問到基類中的同名成員那怎麼辦,C++中這樣規定必須在成員名前加上基類名和作用域標識符"::"。
示例總結:
#include "stdafx.h"
#include <iostream>
#include <string>
class C
{
public:
std::string c;
C(){};
C(std::string c);
~C();
void showC();
};
C::C(std::string c)
{
this->c=c;
std::cout<<"構造c值爲"<<c<<"的對象c"<<std::endl;
}
C::~C()
{
std::cout<<"清理c值爲"<<c<<"的對象c"<<std::endl;
}
void C::showC()
{
std::cout<<"c="<<this->c<<std::endl;
}
class D:public C
{
private:
std::string c;//同名數據成員
std::string d;
C c2;
C c3;
public:
D(std::string d,std::string c1,std::string c2);
~D();
void showC();//同名成員函數
void updateC(std::string c);
};
D::D(std::string d,std::string c2,std::string c3):c2(c2),c3(c3),C("ccc1") //參數傳遞給基類,也可以不寫C(c1)或c(c2),那麼就調用了C類中無參構造函數
{
this->d=d;
this->c=c2;//覆蓋了基類的數據成員c,訪問的是派生類的數據數據成員c
std::cout<<"構造對象d"<<std::endl;
}
D::~D()
{
std::cout<<"清理對象d"<<std::endl;
}
void D::showC()//同名成員函數
{
C::showC();//顯式訪問基類成員函數showC();
c2.showC();
std::cout<<"D::c="<<c<<std::endl;//覆蓋了基類的數據成員c
std::cout<<"C::c="<<C::c<<std::endl;//顯式訪問基類數據成員c
std::cout<<"d="<<this->d<<std::endl;
}
void D::updateC(std::string c)
{
C::c=c;//顯式訪問基類數據成員c
}
int main()
{
{
D d("ddd","ccc2","ccc3");
d.showC();//訪問的是派生類中函數成員showC()
d.updateC("ccc4");
d.showC();//同理
}
return0;
}
結果: