C++第四章面向对象2-继承和多态

114 阅读4分钟

01.继承

面向对象的四个特征之一、

1.继承方式

 [注1]父类的private成员不会被子类继承
 [注2]继承不可能升级,只能降级或平级

|继承方式|父类public成员|父类protected成员|父类private成员| | --- | --- || --- | --- | | 子类public继承| public | protected |不可见| | 子类protected继承|protected| protected| 不可见 | | 子类private继承| private | private | 不可见 |

2.类的派生与继承

1.父类A

class A{
public:
    int nA;
    A(){
        nA = 1;     
    }
    void funcA(){
        cout << "funcA\n";
    }
};

2.子类B继承父类A

class B:public A{
public:
    int nB;
    B(){
        nB = 2;     
    }
    void funcB(){
        cout << "funcB\n";
    }
};

3.子类C继承父类B

class C:public B{
public:
    int nC;
    C(){
        nC = 3;     
    }
    void funcC(){
        cout << "funcC\n";
    }
};

4.完整代码

[注]相当于子类C中可以使用自己的方法之外,还可以使用父类A和父类B的方法
#include <iostream>

using namespace std; 

class A{
public:
    int nA;
    A(){
        nA = 1;     
    }
    void funcA(){
        cout << "funcA\n";
    }
};

class B:public A{
public:
    int nB;
    B(){
        nB = 2;     
    }
    void funcB(){
        cout << "funcB\n";
    }
};

 
class C:public B{
public:
    int nC;
    C(){
        nC = 3;     
    }
    void funcC(){
        cout << "funcC\n";
    }
};

int main() { 
    C c;
    c.funcA();
    c.funcB();
    c.funcC();
    cout << c.nA << endl; 
    cout << c.nB << endl;
    cout << c.nC << endl;
}

3.父子同名成员并存

[注1]子类中同时有两个n和两个func()
[注2]直接使用默认指子类成员
[注3]如果需要使用父类的成员,需要使用父类名字空间显示指明

1.父类

class Father{
public:
    //有一个n,有一个func()函数
    int n = 1;
    void func(){
        cout<<"this is father"<<endl;
    }
};

2.子类

class Son : public Father{
public:
    //有一个n,有一个func()函数,还有一个set()函数
    int n = 2;
    void func(){
        cout<<"this is son"<<endl;
    }
    void set(){
        Father::n = -1;
        n = -2;
    }
};

3.main函数

Son son;

son.func();
son.Father::func();

cout << son.Father::n << endl;
cout << son.n << endl;

son.set();

son.func();
son.Father::func();

cout << son.Father::n << endl;
cout << son.n << endl;

4.完整代码

class Father{
public:
    //有一个n,有一个func()函数
    int n = 1;
    void func(){
        cout<<"this is father"<<endl;
    }
};

class Son : public Father{
public:
    //有一个n,有一个func()函数,还有一个set()函数
    int n = 2;
    void func(){
        cout<<"this is son"<<endl;
    }
    void set(){
        Father::n = -1;
        n = -2;
    }
};

int main(){
Son son;

son.func();
son.Father::func();

cout << son.Father::n << endl;
cout << son.n << endl;

son.set();

son.func();
son.Father::func();

cout << son.Father::n << endl;
cout << son.n << endl;
}

02.虚函数

虚函数:父类的虚函数可以在子类中被重写,即重新实现,但参数和返回值必须保持一致!

1.含有虚函数的类叫做虚类

class Human{
public:
    virtual void say(){
        cout << "I am human";
    }
};

2.父类的虚函数可以在子类中被重写

class Student:public Human{
public:
    void say(){
        cout<<"I am student";
    }
};

3.纯虚函数和抽象类

纯虚函数:不实现,仅声明为纯虚函数,留待子类里重写定义
含有纯虚函数的类叫做抽象类,仅有纯虚函数的类叫接口

1.纯虚函数

例如virtual float getS() = 0;

2.抽象类

class Shape{
public:
    //纯虚函数
    virtual float getS() = 0;
    virtual float getC() = 0;
};

3.完整代码

#include <iostream>

using namespace std; 
//抽象类
class Shape{
public:
    //纯虚函数
    virtual float getS() = 0;
    virtual float getC() = 0;
};

class Circle : public Shape{
private:
    float radius;
public:
    Circle(float radius){
        this->radius = radius;
    }
    float getS(){
        return 3.14 * radius * radius;
    }
    float getC(){
        return 2 * 3.14 * radius;
    }
};

int main(){
    Circle c(3.0);
    cout << c.getC() << endl; 
    cout << c.getS() << endl;
    return 0;
} 

03.多态(重点)

利用虚函数重写+指针(重点)
指向子类对象的父类指针(重点)

1.多态的例子1

#include <iostream>

using namespace std; 

class Human{
public:
    virtual void say(){
        cout << "I am human" << endl;
    }
};

class Student : public Human{
public:
    virtual void say(){
        cout << "I am student"<< endl;
    }
};

class CollegeStudent : public Human{
public:
    virtual void say(){
        cout << "I am college student"<< endl;
    }
};


int main(){
    CollegeStudent a;
    Human* p1 = (Human*) &a;
    Student* p2 = (Student*) &a;
    CollegeStudent* p3 = &a;

    p1->say();//I am college student
    p2->say();//I am college student
    p3->say();//I am college student

    return 0;
} 

2.多态的例子2

#include <iostream>

using namespace std; 

class Human{
public:
    virtual void toilet() = 0;
};

void func(Human *human){
    human->toilet();
} 

class Man : public Human{
public:
    void toilet(){
        cout << "我去男厕所" << endl; 
    }
};

class Woman : public Human{
public:
    void toilet(){
        cout << "我去女厕所" << endl; 
    }
};

class Non : public Human{
public:
    void toilet(){
        cout << "我去无性别厕所" << endl; 
    }
};

int main(){
    Man man1,man2;
    Woman woman1,woman2;
    Non non1,non2;

    func(&man1);
    func(&man2);
    func(&woman1);
    func(&woman2);
    func(&non1);
    func(&non2);
    return 0;
} 

3.多态的意义:代码复用

动态联编 = 运行时多态 = 虚函数重写 = override
父类指定要做什么,子类来实现具体做法
动态联编:在编译时确定了要调用的是哪个函数(根据多个重载函数的参数列表确定)

#include <iostream>

using namespace std; 

class Shape{
public:
    virtual float getC() = 0;
    virtual float getS() = 0;
};

class Circle : public Shape{
	
private:
    float radius;

public:
    Circle(float radius){
    this->radius = radius;
    }

    float getC(){
        return 2 * 3.14 * radius;
    }
    float getS(){
        return 3.14 * radius * radius;
    }
};

class Rectangle : public Shape{
private:
    float a;
    float b;

public:
    Rectangle(float a,float b){
    this->a = a;
    this->b = b;
}
    float getC(){
        return 2*(a+b);
    }
    float getS(){
        return a*b;
    }
};

void display(Shape* ptr){
    //此处实现了多态:通过父类指针调用子类重写的虚函数 
    cout << "C " << ptr->getC() << endl; 
    cout << "S " << ptr->getS() << endl; 
} 


int main(){
    Circle circle(3.0f);
    Rectangle rectangle(3.0,4.0);

    display(&circle);
    display(&rectangle);

    return 0;
} 

4.静态联编与动态联编

联编:确定具体要调用多个同名函数中的哪一个
上述虚函数重写+指针实现的多态特指运行时多态,与之相对的是编译时多态

1.
静态联编 = 编译时多态 = 函数重载 = overload 静态联编:在编译时确定了要调用的是哪个函数(根据多个重载函数的参数列表确定)

动态联编 = 运行时多态 = 虚函数重写 = override
动态联编:直到运行时才知道实际调用的是哪个函数(根据指针指向对象的实际身份)