怎么做網(wǎng)站排名優(yōu)化電子商務(wù)平臺(tái)
1.繼承的概念
繼承是一個(gè)類繼承另外一個(gè)類,稱繼承的類為子類/派生類,被繼承的類稱為父類/基類。
比如下面兩個(gè)類,Student和Person,Student稱為子類,Person稱為父類。
#include<iostream>
using namespace std;class Person
{
public:void identity(){cout << "void identity()" << _name << endl;}public:string _name = "李四";string _tel;string _address;int _age;
};class Student : public Person
{
public:void study(){//...}public:int ID;
};
2.繼承的定義
1.2.1定義格式
1.2.2繼承基類成員訪問(wèn)方式的變化
類成員/繼承方式 | public繼承 | protected繼承 | public繼承 |
基類的public成員 | 派生類的public成員 | 派生類的public成員 | 派生類的public成員 |
基類的protected成員 | 派生類的protected成員 | 派生類的protected成員 | 派生類的protected成員 |
基類的private成員 | 派生類中不可見(jiàn) | 派生類中不可見(jiàn) | 派生類中不可見(jiàn) |
解釋:
- 基類的private無(wú)論以什么方式繼承都不可見(jiàn)。注意:這里的不可見(jiàn)是指基類的私有成員還是被繼承到派生類中,但是語(yǔ)法上限制派生類對(duì)象不管在類里面還是類外面都不能去訪問(wèn)它。
- 如果基類成員不想在類外直接被訪問(wèn)并想在派生類中訪問(wèn),就定義為protected。
- public>protected>private
- 使用關(guān)鍵字class的默認(rèn)繼承方式是private,使用struct默認(rèn)繼承方式是public,但是為了方便閱讀代碼,最好顯示寫(xiě)出繼承方式。(class Student : Person->默認(rèn)私有,struct Student : Person->默認(rèn)共有)
1.3繼承類模版
當(dāng)父類是類模板時(shí),一定注意要指定作用域
#include<iostream>
#include<vector>
using namespace std;
namespace AA
{template<class T>class stack : std::vector<T>{public:void push(const T& x){//push_back(x); //會(huì)報(bào)錯(cuò),“push_back”: 找不到標(biāo)識(shí)符//因?yàn)閟tack<int>實(shí)例化時(shí),也間接實(shí)例化了vector<int>,但是模版是按需實(shí)例化,//push_back成員函數(shù)未實(shí)例化,所以找不到//所以當(dāng)基類是類模板時(shí),需要指定一下類域vector<T>::push_back(x);}//...};
}int main()
{AA::stack<int> st;//自動(dòng)調(diào)用stack的構(gòu)造l;st.push(1);st.push(2);return 0;
}
?3.基類和派生類之間的轉(zhuǎn)換
- public繼承的派生對(duì)象可以賦值給基類的指針/引用。基類的指針/引用指向的是派生類中基類的那一部分。(并沒(méi)有發(fā)生類型轉(zhuǎn)換)
- 基類對(duì)象不能賦值給派生類對(duì)象
class Person {protected :string _name; // 姓名string _sex; // 性別int _age; // 年齡 }; class Student : public Person { public :int _No ; // 學(xué)號(hào) };int main() {Student sobj ;// 派?類對(duì)象可以賦值給基類的指針/引?Person* pp = &sobj;Person& rp = sobj;return 0; }
4.繼承中的作用域
4.1隱藏規(guī)則:
- 在繼承體系中基類和派生類都用獨(dú)立的作用域。
- 派生類和基類有同名成員,派生類成員將屏蔽基類對(duì)同名成員的直接訪問(wèn),這種情況叫隱藏。(在派生類中,可以使用基類::基類成員顯示訪問(wèn))
- 注意如果是成員函數(shù)的隱藏,只需要函數(shù)名相同就構(gòu)成子類隱藏父類。
class Person { protected:string _name = "張三";int _id = 9090; };class Student : public Person { public:void Print(){cout << _name << endl;cout << _id << endl;//生成子類對(duì)應(yīng)9999cout << Person::_id << endl;//生成父類對(duì)應(yīng)9090} protected:int _id = 9999; };int main() {Student stu;stu.Print();return 0; }
5.派生類的默認(rèn)成員函數(shù)
5.1? 6個(gè)常見(jiàn)的默認(rèn)成員函數(shù)?
1.? 派生類的構(gòu)造函數(shù)必須調(diào)用基類的構(gòu)造函數(shù)初始化基類的那一部分成員。如果基類沒(méi)有默認(rèn)的構(gòu)造函數(shù),則必須在派生類構(gòu)造函數(shù)的初始化列表階段顯示調(diào)用。
class Person
{
public:Person(const char* name = "peter"): _name(name){cout << "Person()" << endl;}
protected:string _name;
};class Student : public Person
{
public://默認(rèn)生成的構(gòu)造函數(shù)//1、內(nèi)置類型->看編譯器,不確定//2、自定義類型->調(diào)用默認(rèn)構(gòu)造//3、繼承父類成員看做一個(gè)中體對(duì)象,要求調(diào)用父類的默認(rèn)構(gòu)造
protected:int _num;string _address;
};int main()
{Student stu;return 0;
}
? ? ? ? i. 調(diào)試結(jié)果符合派生類派生類的構(gòu)造函數(shù)必須調(diào)用基類的構(gòu)造函數(shù)初始化基類的那一部分成員?
? ? ? ? ii.? 如果基類沒(méi)有默認(rèn)構(gòu)造函數(shù),則必須在派生類構(gòu)造函數(shù)的初始化列表階段顯示調(diào)用
class Person
{
public:Person(const char* name): _name(name){cout << "Person()" << endl;}
protected:string _name;
};class Student : public Person
{
public://默認(rèn)生成的構(gòu)造函數(shù)//1、內(nèi)置類型->看編譯器,不確定//2、自定義類型->調(diào)用默認(rèn)構(gòu)造//3、繼承父類成員看做一個(gè)中體對(duì)象,要求調(diào)用父類的默認(rèn)構(gòu)造Student(const char* name, int num, const char* address):Person(name)//顯式調(diào)用,_num(num),_address(address){}
protected:int _num;string _address;
};int main()
{Student stu("王五",78,"二七區(qū)");return 0;
}
2. 派生類的拷貝構(gòu)造必須調(diào)用基類的拷貝構(gòu)造完成基類的拷貝初始化。
class Person
{
public:Person(const char* name): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}
protected:string _name;
};class Student : public Person
{
public://默認(rèn)生成的構(gòu)造函數(shù)//1、內(nèi)置類型->看編譯器,不確定//2、自定義類型->調(diào)用默認(rèn)構(gòu)造//3、繼承父類成員看做一個(gè)中體對(duì)象,要求調(diào)用父類的默認(rèn)構(gòu)造Student(const char* name, int num, const char* address): Person(s)//當(dāng)做整體對(duì)象,調(diào)用父類拷貝構(gòu)造,同時(shí),這里還有基類和派生類的轉(zhuǎn)換,_num(num),_address(address){}//嚴(yán)格來(lái)說(shuō),Student的拷貝構(gòu)造不用自己來(lái)完成,默認(rèn)生成就夠用//除非有需要深拷貝的資源。比如:int* ptr = new int[10];Student(const Student& s): Person(s), _num(s._num),_address(s._address){cout << "Student(const Student& s)" << endl;}
protected:int _num;string _address;
};int main()
{Student stu1("王五",78,"二七區(qū)");Student stu2(stu1);return 0;
}
3. 派生類的operator=必須要調(diào)用基類的operator=完成operator=完成基類的賦值。但是要注意:派生類的operator=隱藏了父類的operator=,所以顯示調(diào)用時(shí),需要指定作用域。
class Person
{
public:Person(const char* name): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}
protected:string _name;
};class Student : public Person
{
public://默認(rèn)生成的構(gòu)造函數(shù)//1、內(nèi)置類型->看編譯器,不確定//2、自定義類型->調(diào)用默認(rèn)構(gòu)造//3、繼承父類成員看做一個(gè)中體對(duì)象,要求調(diào)用父類的默認(rèn)構(gòu)造Student(const char* name, int num, const char* address):Person(name),_num(num),_address(address){}//嚴(yán)格來(lái)說(shuō),Student的拷貝構(gòu)造不用自己來(lái)完成,默認(rèn)生成就夠用//除非有需要深拷貝的資源。比如:int* ptr = new int[10];Student(const Student& s): Person(s)//當(dāng)做整體對(duì)象,調(diào)用父類拷貝構(gòu)造,同時(shí),這里還有基類和派生類的轉(zhuǎn)換, _num(s._num),_address(s._address){cout << "Student(const Student& s)" << endl;}//嚴(yán)格來(lái)說(shuō),Student的賦值重載不用自己來(lái)完成,默認(rèn)生成就夠用//除非有需要深拷貝的資源。比如:int* ptr = new int[10];Student& operator=(const Student& s){cout << "Student& operator= (const Student& s)" << endl;if (this != &s){Person::operator=(s);//顯示調(diào)用注意加作用域_num = s._num;_address = s._address;}return *this;}
protected:int _num;string _address;
};int main()
{Student stu1("王五",78,"二七區(qū)");Student stu2(stu1);Student stu3("李四", 90, "高新區(qū)");stu1 = stu3;return 0;
}
4. 派生類的析構(gòu)函數(shù)會(huì)在被調(diào)用完成后自動(dòng)調(diào)用基類的析構(gòu)函數(shù)清理基類成員。因?yàn)檫@樣才能保證派生類對(duì)象先清理派生類成員再清理基類成員。順序是:派生類對(duì)象析構(gòu)先調(diào)用派生類析構(gòu)再調(diào)用基類的析構(gòu)。
class Person
{
public:Person(const char* name): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name;
};class Student : public Person
{
public://默認(rèn)生成的構(gòu)造函數(shù)//1、內(nèi)置類型->看編譯器,不確定//2、自定義類型->調(diào)用默認(rèn)構(gòu)造//3、繼承父類成員看做一個(gè)中體對(duì)象,要求調(diào)用父類的默認(rèn)構(gòu)造Student(const char* name, int num, const char* address):Person(name),_num(num),_address(address){}//嚴(yán)格來(lái)說(shuō),Student的拷貝構(gòu)造不用自己來(lái)完成,默認(rèn)生成就夠用//除非有需要深拷貝的資源。比如:int* ptr = new int[10];Student(const Student& s): Person(s)//當(dāng)做整體對(duì)象,調(diào)用父類拷貝構(gòu)造,同時(shí),這里還有基類和派生類的轉(zhuǎn)換, _num(s._num),_address(s._address){cout << "Student(const Student& s)" << endl;}//嚴(yán)格來(lái)說(shuō),Student的賦值重載不用自己來(lái)完成,默認(rèn)生成就夠用//除非有需要深拷貝的資源。比如:int* ptr = new int[10];Student& operator=(const Student& s){cout << "Student& operator= (const Student& s)" << endl;if (this != &s){Person::operator=(s);//顯示調(diào)用注意加作用域_num = s._num;_address = s._address;}return *this;}//嚴(yán)格來(lái)說(shuō),Student的析構(gòu)函數(shù)不用自己來(lái)完成,默認(rèn)生成就夠用//除非有需要顯示釋放的資源。比如:int* ptr = new int[10];~Student(){Person::~Person();cout << "~Student()" << endl;}
protected:int _num;string _address;
};int main()
{Student stu1("王五",78,"二七區(qū)");return 0;
}
當(dāng)沒(méi)有顯示調(diào)用時(shí),順序是先調(diào)用派生類的析構(gòu)再調(diào)用父類的析構(gòu)。如果顯示調(diào)用,看代碼如何寫(xiě),并不能保證先子后父。
6. 派生類對(duì)象初始化先調(diào)用基類構(gòu)造再調(diào)用派生類構(gòu)造。
總結(jié)來(lái)看,順序如下:
5.2實(shí)現(xiàn)一個(gè)不能被繼承的類
法一:把父類的構(gòu)造函數(shù)私有,子類的構(gòu)成必須調(diào)用基類的構(gòu)造函數(shù),但是父類的構(gòu)造被私有化后,子類就無(wú)法調(diào)用,這樣一來(lái),子類就無(wú)法實(shí)例化出對(duì)象。
法二:C++11新增了一個(gè)final關(guān)鍵字,子類就不能繼承了
class A final//法二加final關(guān)鍵字
{
public:void func1() { cout << "A::func1" << endl; }
protected:int a = 1;
private:// 法一/*A(){}*/
};class B :public A
{void func2() { cout << "B::func2" << endl; }
protected:int b = 2;
};int main()
{A a;B b;return 0;
}
?over~