柚子快報激活碼778899分享:java c++之繼承(上)
柚子快報激活碼778899分享:java c++之繼承(上)
c++之繼承
1. 繼承的概念及定義1.1 繼承的概念1.2 繼承定義1.2.1 定義格式1.2.2 繼承基類成員訪問?式的變化
1.3 繼承類模板
2. 基類和派?類間的轉(zhuǎn)換3. 繼承中的作?域3.1 隱藏規(guī)則:3.2 考察繼承作?域相關(guān)選擇題3.2.1 A和B類中的兩個func構(gòu)成什么關(guān)系()3.2.1 下?程序的編譯運?結(jié)果是什么()
4. 派?類的默認成員函數(shù)4.1 4個常?默認成員函數(shù)
1. 繼承的概念及定義
1.1 繼承的概念
繼承(inheritance)機制是?向?qū)ο蟪绦蛟O(shè)計使代碼可以復(fù)?的最重要的?段,它允許我們在保持原有 類特性的基礎(chǔ)上進?擴展,增加?法(成員函數(shù))和屬性(成員變量),這樣產(chǎn)?新的類,稱派?類。繼承 呈現(xiàn)了?向?qū)ο蟪绦蛟O(shè)計的層次結(jié)構(gòu),體現(xiàn)了由簡單到復(fù)雜的認知過程。以前我們接觸的函數(shù)層次的 復(fù)?,繼承是類設(shè)計層次的復(fù)?。
簡單的來說繼承就是對代碼的復(fù)用,例如下面的代碼:
class student
{
public:
private:
string _name;
string _address;
string _tel;
int _age;
int _stuid;//學(xué)號
};
class teacher
{
public:
private:
string _name;
string _address;
string _tel;
int _age;
string _tea;//職稱
};
teacher這個類與student這個類有共同的成員變量,甚至我們也可以給定一些共同的成員函數(shù),大家都知道重復(fù)不是好的方式,為了解決重復(fù)問題,之前的學(xué)習(xí)引入了模版的概念,但是在這里不能用模版,因為模版是兩個相同的東西,只有操作數(shù)的類型不一樣,而這里是有不同的東西的。
class person
{
public:
// 進?校園/圖書館/實驗室刷?維碼等?份認證
void identity()
{
cout << "void identity()" << _name << endl;
}
protected:
string _name = "zhangsan";
string _address;
string _tel;
int age;
};//相同的部分,這一部分我們將其復(fù)用于student與teacher里,我們一般稱之為父類或者基類
class student:public person
{
public:
void study()//學(xué)生具有學(xué)習(xí)這個行為
{
}
protected:
int _stuid;//學(xué)號
};
class teacher :public person
{
public:
void teach()//老師具有教書這個行為
{
}
protected:
string _teaid;//教師職稱
};
int main()
{
teacher t;
t.identity();
student s;
s.identity();
return 0;
}
1.2 繼承定義
1.2.1 定義格式
下?我們看到Person是基類,也稱作?類。Student是派?類,也稱作?類。(因為翻譯的原因,所以既叫基類/派?類,也叫?類/?類)
1.2.2 繼承基類成員訪問?式的變化
在日常生活中我們基本只會用到第一列,但在考試是會考其他的,所以我們也要了解。 這里其實也不需要我們?nèi)ケ?,我們將上面的繼承分為兩類: 一、基類的私有成員:所有繼承方式都不可見。 二、基類的其他成員:繼承方式與成員類型取小即可,在這里public>protected>private。例如基類的public成員,private繼承,private小所以在派生類是private成員。
class person
{
public:
// 進?校園/圖書館/實驗室刷?維碼等?份認證
void identity()
{
cout << "void identity()" << _name << endl;
}
private:
string _name = "zhangsan";
protected:
//string _name = "zhangsan";
string _address;
string _tel;
int age;
};//相同的部分,這一部分我們將其復(fù)用于student與teacher里,我們一般稱之為父類或者基類
//teacher與student都是公有繼承,當(dāng)我們的成員變量或函數(shù)是私有成員時,在子類和類外部都不能調(diào)用,
//而當(dāng)其為保護成員時,子類可以調(diào)用,但類外部不能調(diào)用,這里就是c++引入保護成員的意義。
class student:public person
{
public:
void study()//學(xué)生具有學(xué)習(xí)這個行為
{
}
protected:
int _stuid;//學(xué)號
};
class teacher :public person
{
public:
void teach()//老師具有教書這個行為
{
}
protected:
string _teaid;//教師職稱
};
int main()
{
teacher t;
t.identity();
student s;
s.identity();
return 0;
}
1.3 繼承類模板
這里我們用棧來模擬
這是第一種方式,指定底層為vector
namespace TSY
{
template
class stack:public std::vector
//class stack:public Container
{
public:
void push(const T& x)
{
Container
}
void pop()
{
Container
}
const T& top()
{
return Container
}
bool empty()
{
return Container
}
size_t size()
{
return Container
}
};
}
int main()
{
TSY::stack
st.push(1);
st.push(2);
st.push(3);
while (!st.empty())
{
cout << st.top() << " ";
st.pop();
}
return 0;
}
這是第二種方式,可以隨時切換我們的底層存儲結(jié)構(gòu),因為宏本質(zhì)是展開,所以不為影響代碼的正確性,反而更符合c++的封裝
#define Container std::vector
//#define Container std::list
//#define Container std::deque
namespace TSY
{
template
//class stack:public std::vector
class stack:public Container
{
public:
void push(const T& x)
{
Container
}
void pop()
{
Container
}
const T& top()
{
return Container
}
bool empty()
{
return Container
}
size_t size()
{
return Container
}
};
}
int main()
{
TSY::stack
st.push(1);
st.push(2);
st.push(3);
while (!st.empty())
{
cout << st.top() << " ";
st.pop();
}
return 0;
}
2. 基類和派?類間的轉(zhuǎn)換
? public繼承的派?類對象 可以賦值給 基類的指針 / 基類的引?。這?有個形象的說法叫切?或者切割。寓意把派?類中基類那部分切出來,基類指針或引?指向的是派?類中切出來的基類那部分。
? 基類對象不能賦值給派?類對象。
? 基類的指針或者引?可以通過強制類型轉(zhuǎn)換賦值給派?類的指針或者引?。但是必須是基類的指針是指向派?類對象時才是安全的。這?基類如果是多態(tài)類型,可以使?RTTI(Run-Time TypeInformation)的dynamic_cast 來進?識別后進?安全轉(zhuǎn)換。(ps:這個我們后?類型轉(zhuǎn)換章節(jié)再單獨專?講解,這?先提?下)
class person
{
public:
// 進?校園/圖書館/實驗室刷?維碼等?份認證
void identity()
{
cout << "void identity()" << _name << endl;
}
private:
string _name = "zhangsan";
protected:
//string _name = "zhangsan";
string _address;
string _tel;
int age;
};
class student:public person
{
public:
void study()//學(xué)生具有學(xué)習(xí)這個行為
{
}
protected:
int _stuid;//學(xué)號
};
class teacher :public person
{
public:
void teach()//老師具有教書這個行為
{
}
protected:
string _teaid;//教師職稱
};
int main()
{
student stu;
person* stu1 = &stu;
person& stu2 = stu;
person stu3 = stu;
//子類給父類可以
//父類給子類會出現(xiàn)問題
person ps;
//student stt1 = ps;
//student stt2 = (student)ps;//強轉(zhuǎn)也會出問題
//支持父類的指針轉(zhuǎn)換
student* stt3 = (student*)stu1;
//student* stt4 = dynamic_cast
return 0;
}
3. 繼承中的作?域
3.1 隱藏規(guī)則:
–
在繼承體系中基類和派?類都有獨?的作?域。派?類和基類中有同名成員,派?類成員將屏蔽基類對同名成員的直接訪問,這種情況叫隱藏。 (在派?類成員函數(shù)中,可以使? 基類::基類成員 顯?訪問)需要注意的是如果是成員函數(shù)的隱藏,只需要函數(shù)名相同就構(gòu)成隱藏。注意在實際中在繼承體系??最好不要定義同名的成員。 –
class person
{
public:
void print()//同名函數(shù)
{
cout << "person::()" << endl;
cout << _size;
}
private:
string _name = "zhangsan";
protected:
//string _name = "zhangsan";
string _address;
string _tel;
int age;//同名成員變量
int _size = 100;
};
class student :public person
{
public:
void print()//同名函數(shù)
{
cout << "student::()" << endl;
cout << _size;
}
protected:
int _stuid;//學(xué)號
int _size = 1000;//同名成員變量
};
int main()
{
student stu;
stu.print();
return 0;
}
3.2 考察繼承作?域相關(guān)選擇題
3.2.1 A和B類中的兩個func構(gòu)成什么關(guān)系()
A. 重載 B. 隱藏 C.沒關(guān)系
3.2.1 下?程序的編譯運?結(jié)果是什么()
A. 編譯報錯 B. 運?報錯 C. 正常運?
class A
{
public:
void fun()
{
cout << "func()" << endl;
}
};
class B : public A
{
public:
void fun(int i)
{
cout << "func(int i)" <
}
};
int main()
{
B b;
b.fun(10);
b.fun();
return 0;
};
這里只需要牢記我們上面的基本原則基本不會出錯。
4. 派?類的默認成員函數(shù)
4.1 4個常?默認成員函數(shù)
6個默認成員函數(shù),默認的意思就是指我們不寫,編譯器會變我們?動?成?個,那么在派?類中,這?個成員函數(shù)是如何?成的呢?
派?類的構(gòu)造函數(shù)必須調(diào)?基類的構(gòu)造函數(shù)初始化基類的那?部分成員。如果基類沒有默認的構(gòu)造函數(shù),則必須在派?類構(gòu)造函數(shù)的初始化列表階段顯?調(diào)?。 派?類的拷?構(gòu)造函數(shù)必須調(diào)?基類的拷?構(gòu)造完成基類的拷?初始化。 派?類的operator=必須要調(diào)?基類的operator=完成基類的復(fù)制。需要注意的是派?類的operator=隱藏了基類的operator=,所以顯?調(diào)?基類的operator=,需要指定基類作?域 派?類的析構(gòu)函數(shù)會在被調(diào)?完成后?動調(diào)?基類的析構(gòu)函數(shù)清理基類成員。因為這樣才能保證派?類對象先清理派?類成員再清理基類成員的順序。 派?類對象初始化先調(diào)?基類構(gòu)造再調(diào)派?類構(gòu)造。 派?類對象析構(gòu)清理先調(diào)?派?類析構(gòu)再調(diào)基類的析構(gòu)。 因為多態(tài)中?些場景析構(gòu)函數(shù)需要構(gòu)成重寫,重寫的條件之?是函數(shù)名相同(這個我們多態(tài)章節(jié)會講解)。那么編譯器會對析構(gòu)函數(shù)名進?特殊處理,處理成destructor(),所以基類析構(gòu)函數(shù)不加virtual的情況下,派?類析構(gòu)函數(shù)和基類析構(gòu)函數(shù)構(gòu)成隱藏關(guān)系。
構(gòu)造,拷貝構(gòu)造,復(fù)制構(gòu)造,析構(gòu)
class Person
{
public:
Person(const char* name = "peter")
: _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:
student(const char*name,int num):
Person(name),_num(num){}//構(gòu)造函數(shù)一般情況是不需要寫的,這里的person我們可以將其理解為內(nèi)置類型
//什么情況需要我們寫呢?例如我們前面類里學(xué)的需要額外申請空間的時候需要寫,寫的時候必須傳name調(diào)用父類的構(gòu)造
// 嚴格說Student拷貝構(gòu)造默認生成的就夠用了
// 如果有需要深拷貝的資源,才需要自己實現(xiàn)
student(const student& stu):
Person(stu),_num(stu._num)
{
}//這里就用到上面的切片的概念
// 嚴格說Student賦值重載默認生成的就夠用了
// 如果有需要深拷貝的資源,才需要自己實現(xiàn)
student operator=(const student& stu)
{
if (this != &stu)
{
Person::operator=(stu);
_num = stu._num;
}
}
~student()
{
//這里會自動調(diào)用父類的析構(gòu)函數(shù),這是為了不破壞先析構(gòu)子類再父類的順序
//如果交由使用者的可能會出現(xiàn)先析構(gòu)父類再析構(gòu)子類的情況。
}
protected:
int _num;//學(xué)號
};
int main()
{
return 0;
}
這里需要注意的是第七點,析構(gòu)在反匯編里會被轉(zhuǎn)換為destructor,與基類構(gòu)成隱藏。
好了繼承第一節(jié)內(nèi)容就簡單的完結(jié)<^^>了。
柚子快報激活碼778899分享:java c++之繼承(上)
相關(guān)閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。