类与对象——Inheritance(继承)
Inheritance (继承)
|
继承链上的类的对应叫法 |
|
|
基类 / Base Class |
派生类 / Derived Class |
|
父类 / Parent Class |
子类 / Child Class |
|
超类 / SuperClass |
子类 / SubClass |
|
继承 vs 泛化 |
|
|
继承/Inherit |
子继承父 |
|
泛化/Generalize |
父泛化子 |
Accessibility (Visibility)——访问控制 (可见性控制)
#include <iostream> using namespace std; class A { public: // 访问属性 int i; protected: int j; private: int k; }; class B : public A { // 此public为派生方式 public: // 访问属性 void display() { cout << i << endl; // OK, can access i cout << j << endl; // OK, can access j cout << k << endl; // Error! cannot access k } }; int main() { A a; cout << a.i << endl; // OK, can access a.i cout << a.j << endl; // Error, cannot access a.j cout << a.k << endl; // Error, cannot access a.k }
继承的方式
公有继承
class Derived:public Base { 派生类新成员定义; };
- 基类成员 在派生类中的访问属性不变。
- 派生类的成员函数 可以访问基类的公有成员和保护成员,不能访问基类的私有成员;
- 派生类以外的其它函数 可以通过派生类的对象,访问从基类继承的公有成员, 但不能访问从基类继承的保护成员和私有成员。
私有继承
class Derived:private Base { 派生类新成员定义; };
- 基类成员 在派生类中都变成 private。
- 派生类的成员函数 可以访问基类的公有成员和保护成员,不能访问基类的私有成员;
- 派生类以外的其它函数 不能通过派生类的对象,访问从基类继承的任何成员。
保护继承
class Derived:protected Base { 派生类新成员定义; };
- 基类成员 公有成员和保护成员变成protected,私有成员不变。
- 派生类的成员函数 可以访问基类的公有成员和保护成员,不能访问基类的私有成员;
- 派生类以外的其它函数 不能通过派生类的对象,访问从基类继承的任何成员。
继承中的构造函数
- C++11中派生类不继承 析构函数、友元函数。
- 继承基类构造函数 ( 使用 using A::A; 语句, 继承所有基类ctor ,不能仅继承指定的某个基类ctor )
struct A { // 等价于 class A { public: A(int i) {} A(double d, int i) {} // ... }; struct B : A { // C++11 using A::A; // 继承基类所有构造函数 int d{0}; // 就地初始化 }; int main() { B b(1); // 调A(int i) }
若派生类成员也需要初始化,则可以在派生类构造函数中调用基类构造函数
struct B : A { // C++11 using A::A; // 继承基类ctor,除了A(int i) int d{0}; // 就地初始化 B(int i) : A{i}, d{i}//注意调用时的顺序一定是先基类,跟编写顺序无关 { std::cout << "B(int i)" << std::endl; } }; int main() { B b(1); // 调用 B(int i) std::cin.get(); }
例子:
#include <iostream> using std::cout; using std::endl; class B { public: B() { cout << "B()" << endl; } B(int i) { cout << "B(" << i << ")" << endl; } B(char c) { cout << "B(" << c << ")" << endl; } }; class E { public: E() { cout << "E()" << endl; } }; class D : public B { public: using B ::B; // D():B() {} // D(int i) {} // D(char c) {} D(double x) : e1{}, e2{}, B(static_cast<int>(x)) { cout << "D(" << x << ")" << endl; } private: E e1, e2; }; int main() { B b; D d; D d2(3.03); std::cin.get(); }

继承中的默认构造函数
若基类ctor未被显式调用,基类的默认构造函数就会被调用

#include <iostream> using std ::cout; using std ::endl; // 任务:基类默认构造函数的作用 // B:public A; class A { //base class public: A() { cout << "A()" << endl; }//然后注释这条语句,加上下一条语句,不加编译器会报错 //A()=default; A(int i) { cout << "A(" << i << ")" << endl; } }; class B : public A { public: B() { cout << "B()" << endl; } B(int j) { cout << "B(" << j << ")" << endl; } }; int main() { A a1{}; A a2{1}; B b1{}; B b2{2}; std::cin.get(); }


final ( C++11 )
C++11引入 final 特殊标识符,避免一个类被继承。
对于代码:
class B final {}; class D : public B {};
编译后的输出是 (Visual Studio) —— error C3246: “D”: 无法从“B”继承,因为它已被声明为“final”这是程序输出
constructor chaining (构造函数链)
构造类实例会沿着继承链调用所有的基类ctor——调用次序: base first, derive next (父先子后)
destructor chaining (析构函数链)
dtor与ctor正好相反——调用次序: derive first, base next (子先父后)
#include <iostream> using std ::cout; using std ::endl; //任务1:创建类结构: Computer->PC- >Desktop/Laptop以及相应的c //main中创建Desktop/Laptop的对象,观察ctor/dtor调用次序 //任务2:增加类Camera作为Laptop的内嵌对象c的类型 //main中创建Laptop对象,观察内嵌对象c的构造与基类构造次序 class Computer { public: Computer() { cout << "Computer()" << endl; } ~Computer() { cout << "~Computer()" << endl; } }; class PC : public Computer { public: PC() { cout << "PC()" << endl; } ~PC() { cout << "~PC()" << endl; } }; class Desktop : public PC { public: Desktop() { cout << "Desktop()" << endl; } ~Desktop() { cout << "~Desktop()" << endl; } }; class Camera { public: Camera() { cout << "Embedded Camera()" << endl; } ~Camera() { cout << " Embedded ~Camera()" << endl; } }; class Laptop : public PC { private: Camera c{}; public: Laptop() { cout << "Laptop()" << endl; } ~Laptop() { cout << "~Laptop()" << endl; } }; int main() { { Desktop d{}; Laptop l{}; } std::cin.get(); }

Name Hiding in Inheritance(继承中的名字隐藏)
对于下面的代码:
class P { public: void f() {} }; class C : public P { public: void f(int x) {} }; int main() { C c; c.f(); }
Visual C++编译结果:
namehiding.cpp(13): error C2660: “C::f ”: 函数不接受 0 个参数
g++编译结果:
NameHiding.cpp:13:7: error: no matching function for call to 'C::f()'
原因:内部作用域的名字隐藏外部作用域的(同名)名字 ——(其中派生类视作内部作用域,基类视作外部作用域),这样做是为了避免某些潜在的危险行为,使每个类在创建时,它的函数名都是写在一张干净的白纸上面,不会被基类函数名干扰。
当然也可以取消隐藏基类同名成员,使用 using 声明语句可以将基类成员引入到派生类定义中。
class P { public: void f() {} }; class C : public P { public: using P::f; //此处不带小括号 void f(int x) {} }; int main() { C c; c.f(); }
Redefining Functions(重定义函数)
Shape::toString() 被Circle继承,因此可以通过Circle对象调用该函数。但基类的toString()无法输出派生类对象信息,所以可以在派生类中重新定义一个toString()函数。
#include <string> std::string Shape::toString() { using namespace std::string_literals; return "Shape color "s + color + ((filled) ? " filled"s : " not filled"s); } string Circle::toString() { return "Circle color " + color + " filled " + ((filled) ? "true" : "false"); } string Rectangle::toString() { return "This is a rectangle object"; }
多重继承
——C++允许一个类从一个或多个基类派生。如果一个类只有一个基类,就称为单一继承。如果一个类具有两个或两个以上的基类,就称为多重继承。
多继承的形式如下:
class 派生类名:[继承方式] 基类名1,[继承方式] 基类名2, … { …… };
其中,继承方式可以是public、protected、private

但是多继承会出现问题:多继承下的二义性
——在多继承方式下,派生类继承了多个基类的成员,当两个不同基类拥有同名成员时,容易产生名字冲突问题。
即:重复基类中派生类间接继承同一基类使得间接基类(Person)在派生类中有多份拷贝,引发二义性。

所以引入概念——虚拟继承
class derived_class : virtual […] base_class
虚基类virtual base class
- 被虚拟继承的基类
- 在其所有的派生类中,仅出现一次

class Salesman : virtual public Employee { public: Salesman(string, Date, int); void setWage(int); void setWkHour(int); void setSales(int); int getSalary() const; protected: float wkHour = 0; int wage = 0; int sales = 0; }; class Manager : virtual public Employee { public: Manager(string, Date, int); int getSalary() const; protected: static int bsWage; }; class SalesManager : public Salesman, public Manager { public: SalesManager(string, CDate, int); int getSalary() const; };
虚基类由最终派生类初始化
- 在没有虚拟继承的情况下,每个派生类的构造函数只负责其直接基类的初始化。但在虚拟继承方式下,虚基类则由最终派生类的构造函数负责初始化。
- 在虚拟继承方式下,若最终派生类的构造函数没有明确调用虚基类的构造函数,编译器就会尝试调用虚基类不需要参数的构造函数(包括缺省、无参和缺省参数的构造函数),如果没找到就会产生编译错误。
虚拟继承的构造次序
- 虚基类的初始化与一般的多重继承的初始化在语法上是一样的,但构造函数的调用顺序不同;
- 若基类由虚基类派生而来,则派生类必须提供对间接基类的构造(即在构造函数初始列表中构造虚基类,无论此虚基类是直接还是间接基类)
调用顺序的规定:
- 先调用虚基类的构造函数,再调用非虚基类的构造函数
- 若同一层次中包含多个虚基类,这些虚基类的构造函数按它们的说明的次序调用
- 若虚基类由非基类派生而来,则仍然先调用基类构造函数,再调用派生类构造函数
#include <iostream> using namespace std; class A { private: int a; public: A(int i) : a(i) { cout << "Constructing A " << i << endl; } }; class B { public: B() { cout << "Constructing B " << endl; } }; class B1 : public B, virtual public A { private: int b1; public: B1(int i) : b1(i), A(i + 1) { cout << "Constructing B1 " << i << endl; } }; class B2 : virtual public A, public B { private: int b2; public: B2(int j) : b2(j), A(j + 1) { cout << "Constructing B2 " << j << endl; } }; class D : public B1, public B2 { public: D(int m, int n) : B1(m), B2(n), a(4), A(5) { cout << "Constructing D" << endl; } private: A a; }; int main() { D d(1, 2); system("pause"); return 0; }

附上一个相关代码:
#include <iostream>//构建的Employee在Date类下面,Worker类上面!(也就是在115行开始 #include <iomanip> #include<string> using std::cout; using std::cin; using std::endl; using std::left; using std::setw; using std::string; #pragma warning(disable : 4996) class Date { public: Date(int, int, int); Date(); void addDay(int); string toString(char); bool setYear(int year_); int getYear() const; bool setMon(int month_); int getMon() const; bool setDay(int day_); int getDay() const; private: int year; int month; int day; static const int maxDay[2][13]; }; const int Date::maxDay[2][13] = { {0,31,28,31,30,31,30,31,31,30,31,30,31},{0,31,29,31,30,31,30,31,31,30,31,30,31} }; Date::Date(int year_, int month_ = 1, int day_ = 1) { if (year_ >= 1900 && year_ <= 9999) if (month_ >= 1 && month_ <= 12) if (day_ >= 1 && day_ <= maxDay[(year_ % 4 == 0 && year_ % 100 != 0) || year_ % 400 == 0][month_]) { year = year_; month = month_; day = day_; } else throw "day is invalid"; else throw "month is invalid!"; else throw "year is invalid!"; } Date::Date() { time_t now; time(&now); struct tm* t_now; t_now = localtime(&now); year = t_now->tm_year + 1900; month = t_now->tm_mon + 1; day = t_now->tm_mday; } void Date::addDay(int n) { day += n; if (day > maxDay[(year % 4 == 0 && year % 100 != 0) || year % 400 == 0][month]) { day %= maxDay[(year % 4 == 0 && year % 100 != 0) || year % 400 == 0][month]; month++; if (month > 12) { year++; month %= 12; } } } string Date::toString(char spor) { return std::to_string(year) + spor + std::to_string(month) + spor + std::to_string(day); } bool Date::setYear(int year_) { if (year_ >= 1900 && year_ <= 2120) { year = year_; return true; } else return false; } int Date::getYear() const { return year; } bool Date::setMon(int month_) { if (month_ >= 1 && month_ <= 12) { month = month_; return true; } else return false; } int Date::getMon() const { return month; } bool Date::setDay(int day_) { if (day_ >= 1 && day_ <= maxDay[(year % 4 == 0 && year % 100 != 0) || year % 400 == 0][month]) { day = day_; return true; } else return false; } int Date::getDay() const { return day; } //为了实现代码重用,现需要你构建类的纵向层次结构,定义Employee类,作为Worker、Saleman和Manager类的基类。 class Employee { public: Employee(string, Date, int); Employee() {}; string getEno() const;//获取员工编号 virtual int getSalary() const{};//计算月薪 int getWkYear() const; //获得入职年限 protected: string eno;//员工编号 Date hiredate;//入职时间 int bsSalary;//底薪 }; Employee::Employee(string _eno, Date hdate, int bsSal) :hiredate(hdate) { eno = _eno; bsSalary = bsSal; } string Employee::getEno() const { return eno; } int Employee::getWkYear() const { Date today; return today.getYear() - hiredate.getYear(); } //工人类 class Worker : public Employee { public: Worker(string _eno, Date hdate, int bsSal) : Employee(_eno, hdate, bsSal){}; void setWage(int); //设置每小时薪酬 void setWkHour(int); //设置月工作时长 int getSalary() const override; //计算月薪 private: float wkHour = 0; //月工作时长(小时) int wage = 0; //每小时薪酬 }; void Worker::setWage(int _wage) { wage = _wage; } void Worker::setWkHour(int _hour) { wkHour = _hour; } int Worker::getSalary() const { int wkYear = getWkYear(); return (bsSalary + wkYear * 200) + wkHour * wage; } //销售人员类 class Salesman : public Employee { public: Salesman(string _eno, Date hdate, int bsSal) : Employee(_eno, hdate, bsSal){}; void setWage(int); //设置每小时薪酬 void setWkHour(int); //设置月工作时长 void setSales(int); //设置月销售额 int getSalary() const override; //计算月薪 private: float wkHour = 0; //月工作时长(小时) int wage = 0; //每小时薪酬 int sales = 0; //月销售额 }; void Salesman::setWage(int _wage) { wage = _wage; } void Salesman::setWkHour(int _hour) { wkHour = _hour; } void Salesman::setSales(int _sales) { sales = _sales; } int Salesman::getSalary() const { int wkYear = getWkYear(); return (bsSalary + wkYear * 200) + wkHour * wage + sales; } //管理人员类 class Manager : public Employee { public: Manager(string _eno, Date hdate, int bsSal) : Employee(_eno, hdate, bsSal){}; int getSalary() const override; //计算月薪 private: static int bsWage; //基本工资 }; int Manager::bsWage = 5000; int Manager::getSalary() const { int wkYear = getWkYear(); return bsWage + (bsSalary + wkYear * 200); } //主函数如下(不得修改): int main() { int bsEmp, bsWk1, bsWk2, bsSal1, bsSal2, bsMg; //员工底薪,工人1底薪,工人2底薪,销售人员1底薪,销售人员2底薪,管理人员1基本工资 cin >> bsEmp >> bsWk1 >> bsWk2 >> bsSal1 >> bsSal2 >> bsMg; cout << left << setw(15) << "ENO" << setw(15) << "SALARY" << endl; cout << "----------------------" << endl; try { Date empHd(2018, 8, 9); Employee emp("e0001", empHd, bsEmp); cout << left << setw(15) << emp.Employee::getEno() << setw(15) << emp.getSalary() << endl; Date wkHd1(2011, 9, 2); Worker wk1("w0001", wkHd1, bsWk1); wk1.setWage(30); wk1.setWkHour(8); cout << left << setw(15) << wk1.Employee::getEno() << setw(15) << wk1.getSalary() << endl; Date wkHd2(2014, 4, 20); Worker wk2("w0002", wkHd2, bsWk2); wk2.setWage(35); wk2.setWkHour(8); cout << left << setw(15) << wk2.Employee::getEno() << setw(15) << wk2.getSalary() << endl; Date salHd1(2003, 7, 2); Salesman sal1("s0001", salHd1, bsSal1); sal1.setWage(50); sal1.setWkHour(8); sal1.setSales(5000); cout << left << setw(15) << sal1.Employee::getEno() << setw(15) << sal1.getSalary() << endl; Date salHd2(2004, 3, 21); Salesman sal2("s0002", salHd2, bsSal2); sal2.setWage(50); sal2.setWkHour(8); sal2.setSales(8000); cout << left << setw(15) << sal2.Employee::getEno() << setw(15) << sal2.getSalary() << endl; Date mgHd1(2001, 5, 10); Manager mg1("m0001", mgHd1, bsMg); cout << left << setw(15) << mg1.Employee::getEno() << setw(15) << mg1.getSalary() << endl; }catch (const char* exp) { cout << exp << endl; } system("pause"); return 0; }



浙公网安备 33010602011771号