国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

C++--第五課補充-繼承

2019-11-10 16:48:12
字體:
來源:轉載
供稿:網友

以員工和經理類為例,闡述繼承的關系

成員變量的覆蓋

猜猜下面的運行結果是什么呢?

#define _CRT_SECURE_NO_WARNINGS#include <iostream>#include <string>using std::string;// 繼承// 為什么要有繼承// 代碼的重用// copy// 組合// 類,封裝// 繼承class Employee{public: Employee(const string &name, const int lev, const int salary_base = 3000) : name_(name), lev_(lev), salary_base_(salary_base) // 構造函數是不能被繼承下來的 { } int Salary() { return salary_base_ * lev_; }PRotected: string name_; int no_; int lev_; int salary_base_;};class Manager : public Employee{public: // 初始化列表中所初始化的變量是自己所擁有的的 // 也就是說在初始化之前是不用有父類中的成員變量的 Manager(const string &name, const int lev, const int salary_base = 5000) : Employee(name, lev, salary_base) { } int Salary() { return static_cast<int>(salary_base_ * lev_ * 1.5); }protected: Employee *employees_; int salary_base_; // 這個變量是重定義,數據的重定義并非覆蓋};int main(){ Employee zs("張三", 10); Manager ls("李四", 10); std::cout << zs.Salary() << std::endl; std::cout << ls.Salary() << std::endl; return 0;}

運行的結果是: 這里寫圖片描述 這個結果是不是令人匪夷所思呢?為什么經理的工資是0呢? 那是因為經理類中有兩個salary_base_的變量,在經理類中只調用Employee類中的構造函數,只是給Employee類中的salary_base_變量賦值,并沒有給經理類中的salary_base_變量賦值,所以才會出現經理的工資是0的情況。改進方法是給經理類中的salary_base_變量也賦值。代碼如下所示:

#define _CRT_SECURE_NO_WARNINGS#include <iostream>#include <string>using std::string;// 繼承// 為什么要有繼承// 代碼的重用// copy// 組合// 類,封裝// 繼承class Employee{public: Employee(const string &name, const int lev, const int salary_base = 3000) : name_(name), lev_(lev), salary_base_(salary_base) // 構造函數是不能被繼承下來的 { } int Salary() { return salary_base_ * lev_; }protected: string name_; int no_; int lev_; int salary_base_;};class Manager : public Employee{public: // 初始化列表中所初始化的變量是自己所擁有的的 // 也就是說在初始化之前是不用有父類中的成員變量的 Manager(const string &name, const int lev, const int salary_base = 5000) : Employee(name, lev), salary_base_(salary_base), employees_(nullptr) { } int Salary() { return static_cast<int>(salary_base_ * lev_ * 1.5); }protected: Employee *employees_; int salary_base_; // 這個變量是重定義,數據的重定義并非覆蓋,但是對于數據成員的重定義本身就是存在問題的};int main(){ Employee zs("張三", 10); Manager ls("李四", 10); std::cout << zs.Salary() << std::endl; std::cout << ls.Salary() << std::endl; return 0;}

這里寫圖片描述 所以,我們以后在管理成員變量的時候,各個類只需要(并且一定要)管理好自己本類中的成員變量即。

成員函數的覆蓋

當經理類中沒有實現Salary的函數時,那么經理類對象調用的Salary函數將是Employee類中的Salary函數,這樣經理對象得到的工資肯定就會少了,代碼如下:

#define _CRT_SECURE_NO_WARNINGS#include <iostream>#include <string>using std::string;// 繼承// 為什么要有繼承// 代碼的重用// copy// 組合// 類,封裝// 繼承class Employee{public: Employee(const string &name, const int lev, const int salary_base = 3000) : name_(name), lev_(lev), salary_base_(salary_base) // 構造函數是不能被繼承下來的 { } int Salary() { return salary_base_ * lev_; }protected: string name_; int no_; int lev_; int salary_base_;};class Manager : public Employee{public: // 初始化列表中所初始化的變量是自己所擁有的的 // 也就是說在初始化之前是不用有父類中的成員變量的 Manager(const string &name, const int lev, const int salary_base = 5000) : Employee(name, lev), salary_base_(salary_base), employees_(nullptr) { } //int Salary() //{ // return static_cast<int>(salary_base_ * lev_ * 1.5); //}protected: Employee *employees_; int salary_base_; // 這個變量是重定義,數據的重定義并非覆蓋};int main(){ Employee zs("張三", 10); Manager ls("李四", 10); std::cout << zs.Salary() << std::endl; std::cout << ls.Salary() << std::endl; return 0;}

結果如下: 這里寫圖片描述 怎么會這樣呢?經理的工資怎么和員工的工資一樣多了呢?經理肯定會不高興的,不但不高興,肯定會不干的。 仔細看代碼不難發現,經理的工資的計算方式和員工的工資的計算方式是一樣的,所以得到的結果也是一樣的,這樣也太不公平了吧!!! 我們在經理類中增加一個帶參數的Salary函數,代碼如下所示:

#define _CRT_SECURE_NO_WARNINGS#include <iostream>#include <string>using std::string;// 繼承// 為什么要有繼承// 代碼的重用// copy// 組合// 類,封裝// 繼承class Employee{public: Employee(const string &name, const int lev, const int salary_base = 3000) : name_(name), lev_(lev), salary_base_(salary_base) // 構造函數是不能被繼承下來的 { } int Salary() { return salary_base_ * lev_; }protected: string name_; int no_; int lev_; int salary_base_;};class Manager : public Employee{public: // 初始化列表中所初始化的變量是自己所擁有的的 // 也就是說在初始化之前是不用有父類中的成員變量的 Manager(const string &name, const int lev, const int salary_base = 5000) : Employee(name, lev), salary_base_(salary_base), employees_(nullptr) { } int Salary(double multiple) // 函數重定義,只要函數名相同就會構成重定義,并且也不是覆蓋 { return static_cast<int>(salary_base_ * lev_ * multiple); }protected: Employee *employees_; int salary_base_; // 這個變量是重定義,數據的重定義并非覆蓋};int main(){ Employee zs("張三", 10); Manager ls("李四", 10); std::cout << zs.Salary() << std::endl; std::cout << ls.Salary() << std::endl; return 0;}

但是,此時編譯就會出問題,錯誤提示如下所示: 這里寫圖片描述 這是為什么呢?員工類中是有無參Salary的函數的呀!!!那為什么經理類中就沒有這個函數了呢?因為Salary函數重定義,導致經理類的對象無法直接使用員工類中的Salary函數,但是也不是說經理類把員工類中的Salary函數給覆蓋了。

#define _CRT_SECURE_NO_WARNINGS#include <iostream>#include <string>using std::string;// 繼承// 為什么要有繼承// 代碼的重用// copy// 組合// 類,封裝// 繼承class Employee{public: Employee(const string &name, const int lev, const int salary_base = 3000) : name_(name), lev_(lev), salary_base_(salary_base) // 構造函數是不能被繼承下來的 { } int Salary() { return salary_base_ * lev_; }protected: string name_; int no_; int lev_; int salary_base_;};class Manager : public Employee{public: // 初始化列表中所初始化的變量是自己所擁有的的 // 也就是說在初始化之前是不用有父類中的成員變量的 Manager(const string &name, const int lev, const int salary_base = 5000) : Employee(name, lev), salary_base_(salary_base), employees_(nullptr) { } int Salary(double multiple) // 函數重定義,只要函數名相同就會構成重定義,并且也不是覆蓋 { return static_cast<int>(salary_base_ * lev_ * multiple); }protected: Employee *employees_; int salary_base_; // 這個變量是重定義,數據的重定義并非覆蓋};int main(){ Employee zs("張三", 10); Manager ls("李四", 10); std::cout << zs.Salary() << std::endl; std::cout << ls.Employee::Salary() << std::endl; //說明不是覆蓋,因為員工類中的Salary函數還是存在的 return 0;}

關于組合和繼承關系的總結

#define _CRT_SECURE_NO_WARNINGS#include <iostream>#include <string>using std::string;// 繼承// 為什么要有繼承// 代碼的重用// copy// 組合// 類,封裝// 繼承class Employee{public: Employee(const string &name, const int lev, const int salary_base = 3000) : name_(name), lev_(lev), salary_base_(salary_base) // 構造函數是不能被繼承下來的 { } int Salary() { return salary_base_ * lev_; }protected: string name_; int no_; int lev_; int salary_base_;};class Manager : public Employee{public: // 初始化列表中所初始化的變量是自己所擁有的的 // 也就是說在初始化之前是不用有父類中的成員變量的 Manager(const string &name, const int lev, const int salary_base = 5000) : Employee(name, lev), salary_base_(salary_base), employees_(nullptr) { } int Salary(double multiple) // 函數重定義,只要函數名相同就會構成重定義,并且也不是覆蓋 { return static_cast<int>(salary_base_ * lev_ * multiple); } // 我們大家都普遍會認為繼承下來的函數會直接放在這個位置 // 比如員工類中的Salary函數放到這里,不就構成重載了嗎? // 那么為什么還不能直接調用無參的Salary函數呢? // int Salary() // { // return salary_base_ * lev_; // } // // 顯然不是這樣的,我們有必要區分一下下面的概念 // 參數不同,能夠構成重載 overlord 重載 相同作用域 // 普通函數(只要函數名相同就會構成重定義) overwrite 重寫,重定義 重定義的話,會默認的使用離你近的 // 虛函數 override 覆蓋protected: Employee *employees_; int salary_base_; // 這個變量是重定義,數據的重定義并非覆蓋};// 這個例子中的Employee類和C++標準庫中的string類是屬于組合關系// Manager類和Employee類是屬于繼承關系// 那么,無論是組合也好,還是繼承也好,這二者作用的結果是一樣的,// 都是將其他類直接放在本類中來使用,只是這二者的表達方式不一樣(設計模式)。// 組合關系一般是 has a 是通過嵌入的方式嵌入到本類中,更多的是希望你來幫我做事情,更多的是新類型暴露出來的一些接口// 比如Employee類,我們要是使用的話,會使用Employee暴露出來的方法,不會使用string類的方法,因為這兩個類已經變成了// 以Employee類為主要功能的一個整體。使用has a來表現這種關系的話,說明我這個類是一個全新的類型,你應該使用我的全新// 的類型進行操作,我的全新的類型里面有一個string類型,它會來幫我完成一些功能,這是has a的關系所表達的意思// 繼承關系一般是 is a,新類和老類之間有一些相同的接口(功能),我們希望把這個功能繼承下來,變成子類型化,有可能會// 對繼承下來的功能進行強化。int main(){ Employee zs("張三", 10); Manager ls("李四", 10); std::cout << zs.Salary() << std::endl; std::cout << ls.Employee::Salary() << std::endl; //說明不是覆蓋,因為員工類中的Salary函數還是存在的 return 0;}

C++中的Operator=運算符是可以被繼承下來的

這個例子主要是想說明C++中的operator=函數是可以被繼承下來的,如果在派生類中重定義了operator=函數,那么它就不會再調用基類中的operator=運算符了,如果沒有重定義operator=運算符,,它會被繼承下來的。

#define _CRT_SECURE_NO_WARNINGS#include <iostream>using std::string;class Employee{public: Employee(const char *name, const int lev, const int salary_base = 3000) : lev_(lev), salary_base_(salary_base) // 構造函數是不能被繼承下來的 { std::size_t len = strlen(name); name_ = new char[len + sizeof(char)]; strcpy(name_, name); } Employee &operator=(const Employee &other) { delete[] name_; std::size_t len = strlen(other.name_); name_ = new char[len + sizeof(char)]; strcpy(name_, other.name_); lev_ = other.lev_; salary_base_ = other.salary_base_; return *this; } const char *GetName() const { return name_; } int Salary() { return salary_base_ * lev_; } int Salary(double multiple) { return static_cast<int>(salary_base_*lev_*multiple); }protected: char *name_; int lev_; int salary_base_;};class Manager : public Employee{public: // 初始化列表中所初始化的變量是自己所擁有的的 // 也就是說在初始化之前是不用有父類中的成員變量的 Manager(const char *name, const int lev, const Employee *employee = nullptr, const int salary_base = 5000) : Employee(name, lev), salary_base_(salary_base) { if (employee) { employees_ = new Employee("", 10); //memcpy(employees_, employee, sizeof(Employee)); // 這樣的拷貝必然是一個淺拷貝,因為Employee類中有指針存在 employees_->operator=(*employee); } } Manager &operator=(const Manager &other) // 重定義 { // 必須也要調用基類的operator=運算符 Employee::operator=(other); if (!employees_) { employees_ = new Employee(*(other.employees_)); } else { *employees_ = *(other.employees_); } return *this; } ~Manager() { delete employees_; } int Salary(double multiple) // 函數重定義,只要函數名相同就會構成重定義,并且也不是覆蓋 { return static_cast<int>(salary_base_ * lev_ * multiple); } Employee *employees_;protected: int salary_base_; // 這個變量是重定義,數據的重定義并非覆蓋};// 不能被繼承的函數// 構造函數 析構函數int main(){ Employee *zs = new Employee("張三", 10); Manager *ls = new Manager("李四", 10); Manager *ww = new Manager("王五", 12, zs); *ls = *ww; // 只調用了子類的operator=運算符 delete zs; delete ww; std::cout << ls->employees_->GetName() << std::endl; std::cout << ls->GetName() << std::endl; //std::cout << zs.Salary() << std::endl; std::cout << ls->Employee::Salary(1.5) << std::endl; //說明不是覆蓋,因為員工類中的Salary函數還是存在的 return 0;}

拷貝構造函數可以被派生類繼承

#define _CRT_SECURE_NO_WARNINGS#include <iostream>using std::string;class Employee{public: Employee(const char *name, const int lev, const int salary_base = 3000) : lev_(lev), salary_base_(salary_base) // 構造函數是不能被繼承下來的 { std::size_t len = strlen(name); name_ = new char[len + sizeof(char)]; strcpy(name_, name); } Employee(const Employee &other) { std::size_t len = strlen(other.name_); name_ = new char[len + sizeof(char)]; strcpy(name_, other.name_); lev_ = other.lev_; salary_base_ = other.salary_base_; } Employee &operator=(const Employee &other) { delete[] name_; std::size_t len = strlen(other.name_); name_ = new char[len + sizeof(char)]; strcpy(name_, other.name_); lev_ = other.lev_; salary_base_ = other.salary_base_; return *this; } const char *GetName() const { return name_; } int Salary() { return salary_base_ * lev_; } int Salary(double multiple) { return static_cast<int>(salary_base_*lev_*multiple); }protected: char *name_; int lev_; int salary_base_;};class Manager : public Employee{public: // 初始化列表中所初始化的變量是自己所擁有的的 // 也就是說在初始化之前是不用有父類中的成員變量的 Manager(const char *name, const int lev, const Employee *employee = nullptr, const int salary_base = 5000) : Employee(name, lev), salary_base_(salary_base) { if (employee) { employees_ = new Employee("", 10); //memcpy(employees_, employee, sizeof(Employee)); // 這樣的拷貝必然是一個淺拷貝,因為Employee類中有指針存在 employees_->operator=(*employee); } } Manager &operator=(const Manager &other) // 重定義 { // 必須也要調用基類的operator=運算符 Employee::operator=(other); if (!employees_) { employees_ = new Employee(*(other.employees_)); } else { *employees_ = *(other.employees_); } return *this; } ~Manager() { delete employees_; } int Salary(double multiple) // 函數重定義,只要函數名相同就會構成重定義,并且也不是覆蓋 { return static_cast<int>(salary_base_ * lev_ * multiple); } Employee *employees_;protected: int salary_base_; // 這個變量是重定義,數據的重定義并非覆蓋};// 不能被繼承的函數// 構造函數 析構函數int main(){ Employee *zs = new Employee("張三", 10); Manager *ls = new Manager("李四", 10); Manager ww = *ls; //Manager *ww = new Manager("王五", 12, zs); //*ls = *ww; // 只調用了子類的operator=運算符 //delete zs; //delete ww; //std::cout << ls->employees_->GetName() << std::endl; // //std::cout << ls->GetName() << std::endl; ////std::cout << zs.Salary() << std::endl; //std::cout << ls->Employee::Salary(1.5) << std::endl; //說明不是覆蓋,因為員工類中的Salary函數還是存在的 return 0;}

公有繼承的類是is a的關系,可以向上轉換,其余的protected和private方式繼承的就不是is a的關系了,也就不能向上轉換了

#define _CRT_SECURE_NO_WARNINGS#include <iostream>using std::string;class Employee{public: Employee(const char *name, const int lev, const int salary_base = 3000) : lev_(lev), salary_base_(salary_base) // 構造函數是不能被繼承下來的 { std::size_t len = strlen(name); name_ = new char[len + sizeof(char)]; strcpy(name_, name); } Employee(const Employee &other) { std::size_t len = strlen(other.name_); name_ = new char[len + sizeof(char)]; strcpy(name_, other.name_); lev_ = other.lev_; salary_base_ = other.salary_base_; } Employee &operator=(const Employee &other) { delete[] name_; std::size_t len = strlen(other.name_); name_ = new char[len + sizeof(char)]; strcpy(name_, other.name_); lev_ = other.lev_; salary_base_ = other.salary_base_; return *this; } const char *GetName() const { return name_; } int Salary() { return salary_base_ * lev_; } int Salary(double multiple) { return static_cast<int>(salary_base_*lev_*multiple); }protected: char *name_; int lev_; int salary_base_;};class Manager : public Employee{public: // 初始化列表中所初始化的變量是自己所擁有的的 // 也就是說在初始化之前是不用有父類中的成員變量的 Manager(const char *name, const int lev, const Employee *employee = nullptr, const int salary_base = 5000) : Employee(name, lev), salary_base_(salary_base) { if (employee) { employees_ = new Employee("", 10); //memcpy(employees_, employee, sizeof(Employee)); // 這樣的拷貝必然是一個淺拷貝,因為Employee類中有指針存在 employees_->operator=(*employee); } } // 雖然說已經繼承下來了基類中的拷貝構造函數,但是我們還是需要重寫派生類中的拷貝構造函數 // 因為基類中的拷貝構造函數對于派生類來說構造的不是很完整 // 并且我們必須在初始化列表中顯示的去調用基類中的拷貝構造函數,那么這樣的話,如果派生類 // 中寫了拷貝構造函數,那么基類中就必須有拷貝構造函數,否則派生類中是無法重定義拷貝構造函數的 // 需要注意的是,為什么派生類的對象能夠直接當成基類的對象來使用呢? // 這是因為公有繼承的 is a 的關系,如果不是以public方式繼承的時候,此時就不是is a的關系了 Manager(const Manager &other) : Employee(other) { if (!other.employees_) { employees_ = new Employee(*(other.employees_)); } } Manager &operator=(const Manager &other) // 重定義 { // 必須也要調用基類的operator=運算符 Employee::operator=(other); if (!employees_) { employees_ = new Employee(*(other.employees_)); } else { *employees_ = *(other.employees_); } return *this; } ~Manager() { delete employees_; } int Salary(double multiple) // 函數重定義,只要函數名相同就會構成重定義,并且也不是覆蓋 { return static_cast<int>(salary_base_ * lev_ * multiple); } Employee *employees_;protected: int salary_base_; // 這個變量是重定義,數據的重定義并非覆蓋};// 不能被繼承的函數// 構造函數 析構函數int main(){ Employee *zs = new Employee("張三", 10); Manager *ls = new Manager("李四", 10); Manager ww = *ls; //Manager *ww = new Manager("王五", 12, zs); //*ls = *ww; // 只調用了子類的operator=運算符 //delete zs; //delete ww; //std::cout << ls->employees_->GetName() << std::endl; // //std::cout << ls->GetName() << std::endl; ////std::cout << zs.Salary() << std::endl; //std::cout << ls->Employee::Salary(1.5) << std::endl; //說明不是覆蓋,因為員工類中的Salary函數還是存在的 return 0;}

另一個簡單的例子來說明原因 public的繼承方式

#define _CRT_SECURE_NO_WARNINGS#include <iostream>class A{};class B : public A{};int main(){ B b; A a = b; // 此時可以直接將派生類的對象轉換為基類的對象 A c; c = b; return 0;}#define _CRT_SECURE_NO_WARNINGS#include <iostream>class A{};class B : public A{public: int b_;};int main(){ A *pa = new A; B *pb = new B; A a; B b; pa = pb; // 派生類向基類轉換,就會產生對象切割(丟失特性) pa->b_; // 此時就無法訪問b_了,這就叫做丟失特性 pb = pa; // 基類不可以向基類轉化 a = b; b = a; return 0;}

剩下的protected和private繼承就無法轉化了。讀者可以自行試驗。 但是我們可以使用強制轉換,但是這是很危險的一件事情,最好不要這樣做。

#define _CRT_SECURE_NO_WARNINGS#include <iostream>class A{};class B : public A{public: int b_;};int main(){ A *pa = new A; B *pb = new B; A a; B b; pa = pb; // 派生類向基類轉換,就會產生對象切割(丟失特性) pa->b_; // 此時就無法訪問b_了,這就叫做丟失特性 pb = pa; // 基類不可以向基類轉化 a = b; b = a; static_cast //做的是編譯器認可的轉化 reinterpret_cast //做的是編譯器不認可的轉換,是二進制直接轉換的 // 指針可以用它來轉換,但是對象無論如何都轉換不了的 return 0;}

不能被繼承下來的函數只有構造函數和析構函數

這里我們就不再舉例子了


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

主站蜘蛛池模板: 开江县| 文登市| 马关县| 屏山县| 如皋市| 通化县| 江永县| 周口市| 渝中区| 罗城| 双鸭山市| 盐津县| 璧山县| 冷水江市| 浠水县| 辽宁省| 蒙自县| 大丰市| 读书| 会泽县| 顺义区| 莱州市| 梓潼县| 南通市| 德安县| 天水市| 长岭县| 珲春市| 上犹县| 唐山市| 太谷县| 石嘴山市| 鹤壁市| 蛟河市| 安塞县| 麻江县| 拉孜县| 河西区| 城市| 邮箱| 苍山县|