以员工和经理类为例,阐述继承的关系
成员变量的覆盖
猜猜下面的运行结果是什么呢?
#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;}不能被继承下来的函数只有构造函数和析构函数
这里我们就不再举例子了