类与对象——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
{
    派生类新成员定义;
};
  1.    基类成员  在派生类中的访问属性不变。
  2.    派生类的成员函数  可以访问基类的公有成员和保护成员,不能访问基类的私有成员;
  3.    派生类以外的其它函数  可以通过派生类的对象,访问从基类继承的公有成员, 但不能访问从基类继承的保护成员和私有成员。

 私有继承

class Derived:private Base
{
    派生类新成员定义;
};
  1.      基类成员    在派生类中都变成 private。
  2.      派生类的成员函数    可以访问基类的公有成员和保护成员,不能访问基类的私有成员;
  3.      派生类以外的其它函数    不能通过派生类的对象,访问从基类继承的任何成员。

 保护继承

class Derived:protected Base
{
    派生类新成员定义;
};
  1.      基类成员  公有成员和保护成员变成protected,私有成员不变。
  2.      派生类的成员函数    可以访问基类的公有成员和保护成员,不能访问基类的私有成员;
  3.      派生类以外的其它函数    不能通过派生类的对象,访问从基类继承的任何成员。

继承中的构造函数

  1. C++11中派生类不继承 析构函数、友元函数。
  2. 继承基类构造函数  ( 使用 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;
};

虚基类由最终派生类初始化

  1. 在没有虚拟继承的情况下,每个派生类的构造函数只负责其直接基类的初始化。但在虚拟继承方式下,虚基类则由最终派生类的构造函数负责初始化。
  2. 在虚拟继承方式下,若最终派生类的构造函数没有明确调用虚基类的构造函数,编译器就会尝试调用虚基类不需要参数的构造函数(包括缺省、无参和缺省参数的构造函数),如果没找到就会产生编译错误。

虚拟继承的构造次序

  • 虚基类的初始化与一般的多重继承的初始化在语法上是一样的,但构造函数的调用顺序不同;
  • 若基类由虚基类派生而来,则派生类必须提供对间接基类的构造(即在构造函数初始列表中构造虚基类,无论此虚基类是直接还是间接基类)

调用顺序的规定:

  1. 先调用虚基类的构造函数,再调用非虚基类的构造函数
  2. 若同一层次中包含多个虚基类,这些虚基类的构造函数按它们的说明的次序调用
  3. 若虚基类由非基类派生而来,则仍然先调用基类构造函数,再调用派生类构造函数
#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;
}

 

posted @ 2020-04-09 17:47  我等着你  阅读(625)  评论(0)    收藏  举报