(四)C++中的继承、多态和模板函数

C++中的继承、多态和模板函数

一、继承

1、属性和方法的继承

继承可以更好的实现代码的重用性

#include <stdlib.h>
#include <iostream>

using namespace std;

//开发者
class Developer {
protected:
    char* language;
    char* ide;
    int age;
public:
    void say() {
        cout << "我是开发者" << endl;
    }
};

//Android开发者
class AndroidDeveloper : public Developer {
public:
    AndroidDeveloper() {
        this->language = (char*)"Android+Kotlin";
        this->ide = (char*)"Android Stuio";
    }
    //开发Android Application
    void createAndroidApp() {
        cout << "我使用" << this->ide << "开发了一款Android应用,使用了" << this->language << "语言" << endl;
    }
private:
    //Android 版本
    char* androidVersion;

};

//所有开发者都有开发工作
void work(Developer& d) {
    d.say();
}

void main() {
    AndroidDeveloper androidDev;
    androidDev.say();
    androidDev.createAndroidApp();
    //子类对象初始化父类类型的对象
    Developer d1 = androidDev;
    work(d1);
    //父类类型的指针
    Developer* d_p = &androidDev;
    d_p->say();
    //父类类型的引用
    Developer d2 = androidDev;
    d2.say();

    getchar();
}

2、通过子类给父类构造方法传参

父类的构造函数先调用;子类的析构函数先调用

//开发者
class Developer {
protected:
    char* language;
    char* ide;
    int age;
public:
    Developer(char * language, char* ide, int age) {
        this->language = language;
        this->ide = ide;
        this->age = age;
        cout << "Developer 构造函数" << endl;
    }
    ~Developer() {
        cout << "Developer 析构函数" << endl;
    }
    void say() {
        cout << "我是开发者" << endl;
    }
};

//Android开发者
class AndroidDeveloper : public Developer {
public:
    AndroidDeveloper(char* language, char* ide, int age, char* androidVersion) : Developer(language, ide, age) {
        this->language = language;
        this->ide = ide;
        this->age = age;
        cout << "AndroidDeveloper 构造函数" << endl;
    }
    ~AndroidDeveloper(){
        cout << "AndroidDeveloper 析构函数" << endl;
    }
    //开发Android Application
    void createAndroidApp() {
        cout << "我使用" << this->ide << "开发了一款Android应用,使用了" << this->language << "语言" << endl;
    }
private:
    //Android 版本
    char* androidVersion;

};

void work(Developer& d) {
    d.say();
}

//父类的构造函数先调用
//子类的析构函数先调用
void func() {
    AndroidDeveloper androidDev((char*)"Kotlin", (char*)"Android Studio", 5, (char*)"5.0.1");
    androidDev.say();
    androidDev.createAndroidApp();
}

void main() {
    func();
    getchar();
}

输出:

Developer 构造函数
AndroidDeveloper 构造函数
我是开发者
我使用Android Studio开发了一款Android应用,使用了Kotlin语言
AndroidDeveloper 析构函数
Developer 析构函数

3、继承中父类和子类的权限继承关系

基类中继承方式子类中
public& public继承=> public
public& protected继承=> protected
public& private继承=> private
protected& public继承=> protected
protected& protected继承=> protected
protected& private继承=> private
private& public继承=> 子类无权访问
private& protected继承=> 子类无权访问
private& private继承=> 子类无权访问

4、继承的二义性

4.1 继承的二义性定义

在某个类B同时继承另一个类A的两个或多个子类时(A1和A2),通过类B访问类A的成员时,会出现成员不明确的情况,即继承的二义性

class A {
public:
    char* name;
};

class A1 : public A {

};

class A2 : public A {

};

class B : public A1, public A2 {

};

void main() {
    B b;
    //报错,提示B::name不明确
    //b.name = (char*)"Jack";

    //指定父类显式调用
    b.A1::name = (char*)"Rose";
    getchar();
}

4.2 继承的二义性定义解决方案

再遇到继承的二义性时,可使用虚继承来解决继承的二义性问题
虚继承:不同路径继承来的同名成员只有一份拷贝

class A {
public:
    char* name;
};

class A1 : virtual public A {

};

class A2 : virtual public A {

};

class B : public A1, public A2 {

};

void main() {
    B b;
    //报错,提示B::name不明确
    //b.name = (char*)"Jack";

    //指定父类显式调用
    b.A1::name = (char*)"Rose";
    getchar();
}

二、多态

  • 多态是为了提高程序的扩展性
  • 动态多态:子类重写父类的函数,程序运行过程中,决定哪一个函数被调用
  • 静态多态:就是函数重载

1、虚函数

virtual 关键字修饰的函数叫虚函数,用来实现多态

例如:

Plane.h

#pragma once

class Plane {
public:
    virtual void fly();
    virtual void land();
};

Plane.cpp

#include "Plane.h"
#include <stdlib.h>
#include <iostream>

using namespace std;
void Plane::fly() {
    cout << "飞机起飞" << endl;
}

void Plane::land() {
    cout << "飞机降落" << endl;
}

Helicopter.h

#pragma once
#include "Plane.h"

class Helicopter : public Plane {
public:
    virtual void fly();
    virtual void land();
};

Helicopter.cpp

#include "Helicopter.h"
#include <stdlib.h>
#include <iostream>

using namespace std;
void Helicopter::fly() {
    cout << "直升飞机在原地起飞" << endl;
}

void Helicopter::land() {
    cout << "直升飞机降落在屋顶" << endl;
}

Test.cpp

#include <stdlib.h>
#include <iostream>
#include "Plane.h"
#include "Helicopter.h"
using namespace std;

//业务函数

void runPlane(Plane &p) {
    p.fly();
    p.land();
}

void main() {
    Plane p;
    
    runPlane(p);
    Helicopter h;
    //在 Plane.h 和 Helicopter.h 中的函数上不使用 virtual 修饰时,打印“飞机起飞”和“飞机降落”
    //使用 virtual 修饰时,打印“直升飞机在原地起飞”和“直升飞机降落在屋顶”,实现多态
    runPlane(h);
    getchar();
}

2、发生动态多态的条件

  • 使用继承
  • 父类的引用或指针指向子类的对象
  • 函数的重写

3、纯虚函数(抽象类)

  • 当一个类具有一个纯虚函数时,这个类就是抽象类
  • 抽象类不能被实例化
  • 子类继承抽象类,必须要实现纯虚函数,如果没有重新,子类也是抽象类
//形状
class Shape {
public:
    virtual void sayArea() = 0;
};

//圆
class Circle : public Shape {
private:
    int r;
public:
    Circle(int r) {
        this->r = r;
    }
    void sayArea() {
        cout << "圆的面积:" << 3.14 * r * r << endl;
    }
};

void main() {
    Circle c(5);
    c.sayArea();
    getchar();
}

4、接口

接口只是逻辑上的划分,语法上跟抽象类的写法没有区别

//可以看作一个接口
class Drawable{
    virtual void draw() = 0;
}

5、抽象类的作用

为了继承约束,子类必须按照约束实现

//可以看作一个接口
class Drawable{
    virtual void draw() = 0;
}

二、模板函数(泛型)

函数模板类似于泛型,用于在业务相同,参数类型不同时进行声明,在使用过程中,根据实际类型进行推导

template <typename T,typename Z>
//交换两个变量的值
void swap(T& a, Z& b){
    T tmp = 0;
    tmp = a;
    a = b;
    b = tmp;
}

void main(){
    int a = 10;
    int b = 25;
    swap(a,b);
    cout << a << "," << b << endl;
    
    char* x = (char*)"abc";
    char* y = (char*)"def";
    swap(x,y);
    cout << x << "," << y << endl;
    
    getchar();
}
    原文作者:onestravel
    原文地址: https://www.jianshu.com/p/db2f4135210b
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞