C++ 中的继承和多态

这篇具有很好参考价值的文章主要介绍了C++ 中的继承和多态。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C++ 中的继承和多态

一、继承

继承允许我们依据一个类来定义另一个类,这使得创建和维护一个应用程序变得更容易。这样做也达到了重用代码功能和提高执行效率的效果。

派生类的成员可以直接访问基类的保护成员(protected),但不能直接访问基类的私有成员(private)。不过需要注意的是,派生类的成员函数只能访问所作用的那个对象(即this指针指向的对象)的基类保护成员,不能访问其他基类对象的基类保护成员(比如通过函数参数传来的基类对象)。

继承分为公有继承、保护继承与私有继承,除了公有继承,剩下两个很少用到,三者区别如下:

C++ 中的继承和多态


二、函数重载、隐藏、覆盖、重写

1.函数重载(Function Overload)

C++规定在同一作用域中函数名相同但函数特征标(即参数个数、类型、顺序)不同时,构成函数重载

函数重载的注意事项:

  1. 返回值类型不能作为重载的标准。
  2. 参数是否为引用不能作为重载的标准,尽管某些时候能通过编译,但在调用时会产生二义性。
  3. 成员函数是否被static修饰也不能作为重载的标准,因为在通过实例化后的对象调用方法时无法区分是否要调用静态成员函数。
  4. 一个函数不能既作为重载函数,又作为有默认参数的函数,因为当调用函数时如果少写一个参数,系统无法判定是利用重载函数还是利用默认参数的函数,即 int func(int a)int func(int a = 0) 是不能在同一作用域中同时存在的。

这里还要特别注意一下const修饰函数或函数参数时的情况:

class A {
public:
    /**
     * 不管形参有没有const修饰实参都不会被修改,二者在调用时没有区别,因此不能构成重载
     */
    void func(int a);
    void func(const int a); // NO

    /**
     * 由底层const修饰的指针指向的实参不能被修改,二者在调用时存在区别,因此可以构成重载
     */
    void func_bot_p(int *a);
    void func_bot_p(const int *a); // YES

    /**
     * 不管有没有顶层const修饰,该指针指向的内容都可以被修改,二者在调用时没有区别,因此不能构成重载
     */
    void func_top_p(int *a);
    void func_top_p(int *const a); // NO

    /**
     * 在const修饰引用时实参不能被修改,二者在调用时存在区别,因此可以构成重载
     */
    void func_ref(int &a);
    void func_ref(const int &a); // YES

    /**
     * 由const修饰的成员函数只能由const对象调用,二者在调用时存在区别,因此可以构成重载
     */
    void func_ret(int a);
    void func_ret(int a) const; // YES
};

2.函数隐藏(Function Hiding)

不同作用域中定义的同名函数会构成函数隐藏(不要求函数返回值和函数参数类型相同)。

类成员函数会屏蔽全局函数派生类成员函数会屏蔽与其同名的基类成员函数(但如果该基类成员函数为虚函数,且函数返回值和特征标相同则构成函数重写)。

#include <iostream>

using namespace std;

void func() {
    cout << "global::func()" << endl;
}

class A {
public:
    /**
     * 隐藏了外部的func
     */
    void func() {
        cout << "A::func()" << endl;
    }

    void use_func() {
        func();
        ::func(); // 使用全局函数时要加作用域
    }
};

class B : public A {
public:
    /**
     * 隐藏了基类的func
     */
    void func() {
        cout << "B::func()" << endl;
    }

    void use_func() {
        func();
        A::func(); // 使用基类函数时要加作用域
    }
};

int main() {
    A a;
    B b;

    a.use_func();
    b.use_func();
}
atreus@MacBook-Pro % g++ main.cpp -o main -std=c++11
atreus@MacBook-Pro % ./main
A::func()
global::func()
B::func()
A::func()
atreus@MacBook-Pro % 

3.函数重写与函数覆盖(Function Override)

派生类中与基类同返回值类型、同名和同特征标虚函数重定义,构成虚函数覆盖,也叫虚函数重写

需要注意的是,在默认情况下,如果重新定义了继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针,这种新出现的特性叫做返回类型协变(covariance of return type)。

#include <iostream>

using namespace std;

class A {
public:
    void func() {
        cout << "A::func()" << endl;
    }

    virtual void func_v() {
        cout << "A::func_v()" << endl;
    }
};

class B : public A {
public:
    /* 函数隐藏 */
    void func() {
        cout << "B::func()" << endl;
    }

    /* 函数重载 */
    void func_v() override {
        cout << "B::func_v()" << endl;
    }
};

int main() {
    A *a = new B;

    a->func();
    a->func_v();

    delete a;
}
atreus@MacBook-Pro % g++ main.cpp -o main -std=c++11 
atreus@MacBook-Pro % ./main
A::func()
B::func_v()
atreus@MacBook-Pro % 

三、多态

多态是指一个方法同时具有多种形态,具体形态取决于调用该方法的具体对象。从实现的角度可以将多态分为编译时多态(主要通过函数模板、函数重载和运算符重载实现)和运行时多态(主要通过虚函数和函数重写实现)。

对于运行时多态,其实现主要有三个前提:

  • 存在继承
  • 存在函数重写(覆盖)
  • 存在基类指针或者引用指向子类对象

运行时多态的实现要借助于动态绑定,动态绑定借助于虚函数实现,虚函数的限制如下:

  • 只有类的成员函数才能声明为虚函数。
  • 基类的析构函数可以是虚函数且通常声明为虚函数。
  • 构造函数不能为虚函数。
  • 内联函数不能是虚函数。
  • 静态成员函数不能是虚函数。

虚函数、虚函数表及虚函数实现多态的原理

其中,动态绑定是运行时绑定,通过地址实现,它是指基类的指针或引用有可能指向不同的派生类对象。对于非虚函数,执行时实际调用该函数的对象类型即为该指针或引用的静态类型。而对于虚函数,执行时实际调用该函数的对象类型为该指针或引用所指对象的实际类型


四、纯虚函数和抽象类

当类声明中包含纯虚函数(定义是末尾有 = 0 的虚函数)时,则不能创建该类的对象,这个类变为抽象类,C++中的抽象类类似于Java中的接口,抽象类必须至少包含一个纯虚函数

此外,对于抽象类还有以下注意事项:

  1. 抽象类只能用作其他类的基类,当然也可以作为另一个抽象类的基类
  2. 抽象类不能用来定义对象,不能实例化,也不能用作参数类型、函数返回类型或显式转换的类型
  3. 如果一个非抽象类从抽象类中派生,则其必须通过覆盖来实现所有的继承而来的抽象成员函数
#include <iostream>

/* 抽象类 */
class Car {
public:
    virtual void showName() = 0; // 纯虚函数
};

class Audi : public Car {
public:
    void showName() override { std::cout << "Audi" << std::endl; }
};

class Volvo : public Car {
public:
    void showName() override { std::cout << "Volvo" << std::endl; }
};

int main() {
    Audi audi;
    Volvo volvo;

    audi.showName(); // Audi
    volvo.showName(); // Volvo

    return 0;
}

五、多重继承的二义性(菱形继承)

菱形继承是指当类B和类C同时继承于基类A,类D同时继承于类B和类C,此时类A中的成员变量和成员函数继承到类D中就变成了两份,在D中调用A中的成员会导致二义性,同时一个变量分两份存储也存在内存空间浪费的问题。

C++ 中的继承和多态

通过虚基类虚继承机制,可以在多继承中只保留一份共同成员,从而解决了多继承导致的命名冲突数据冗余

在继承方式前面加上 virtual 关键字就是虚继承,如果不采用虚继承,在类D中使用类A中的m_a时则需要通过 B::m_aC::m_a 来指定具体使用哪个m_a。

#include <iostream>

using namespace std;

class A {
protected:
    int m_a = 0;
};

class B : virtual public A {
protected:
    int m_b = 1;
};

class C : virtual public A {
protected:
    int m_c = 2;
};

class D : public B, public C {
protected:
    int m_d = 3;

public:
    D() {
        cout << m_a << endl;
        cout << m_b << endl;
        cout << m_c << endl;
        cout << m_d << endl;
    }
};

int main() {
    D d;
    return 0;
}
atreus@MacBook-Pro % g++ main.cpp -o main -std=c++11
atreus@MacBook-Pro % ./main
0
1
2
3
atreus@MacBook-Pro % 

C++标准库中的iostream类就是一个虚继承的实际应用案例。iostream从istream和ostream直接继承而来,而istream和ostream又都继承自一个共同的名为base_ios的类,是典型的菱形继承。

C++ 中的继承和多态


参考:

https://cloud.tencent.com/developer/article/1177174
https://blog.csdn.net/weixin_39640298/article/details/88725073
http://c.biancheng.net/view/2280.html

C++ 中的继承和多态文章来源地址https://www.toymoban.com/news/detail-456899.html

到了这里,关于C++ 中的继承和多态的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】继承和多态

    继承机制是面向对象程序设计使代码可以 复用 的最重要的手段,它允许程序员在保持原有类特性的基础上进行 扩展 ,增加功能,这样产生新的类,称 派生类/子类 。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复

    2024年02月06日
    浏览(42)
  • C++基础篇:07 继承与多态

            当遇到问题,先看一下现有的类是否能够解决一部分问题,如果有则继承,并在此基础上进行扩展来解决所有问题,以此缩短解决问题的时间(代码复用)         当遇到一个大而复杂的问题时,可以把复杂问题拆分成若干个小问题,为每个小问题的解决设计一

    2024年02月08日
    浏览(50)
  • c++面向对象之封装、继承、和多态

    把客观事物封装成类,而且可以把自己的数据和方法设置为只能让可信的类或者对象操作,对不可信的信息进行隐藏(利用public,private,protected,friend)实现 has-a :描述一个类由多个部件类构成,一个类的成员属性是另一个已经定义好的类。 use-a:一个类使用另一个类,通过类之间

    2024年02月02日
    浏览(53)
  • C++ 面向对象核心(继承、权限、多态、抽象类)

    继承(Inheritance)是面向对象编程中的一个重要概念,它允许一个类(称为派生类或子类)从另一个类(称为基类或父类)继承属性和方法。继承是实现类之间的关系,通过继承,子类可以重用父类的代码,并且可以在此基础上添加新的功能或修改已有的功能。 在C++中,继承

    2024年02月08日
    浏览(49)
  • 【C++】继承和多态、共有私有和保护、重写

    继承是一种机制,通过它 一个类可以从另一个类继承属性和方法 。派生类(子类)继承基类(父类)的成员函数和数据成员,并且可以在其基础上扩展自己的成员函数和数据成员。C++支持多重继承,即一个派生类可以同时从多个基类中继承。 多态是指 同一种操作作用于不同

    2024年02月03日
    浏览(40)
  • C++ 第三弹继承和多态-类和对象

    目录 1.继承 1.1什么是继承? 1.2语法格式 1.3继承权限 1.4继承概念语法格式 1.5赋值兼容规则 1.6继承体系中的作用域 1.7在继承体系中的构造和析构 1.8静态成员继承 1.9友元的继承 1.10不同继承方式下子类的对象模型 1.11继承和组合 2.多态 2.1什么是多态 2.2多态的分类 2.3实现条件

    2024年02月10日
    浏览(33)
  • c++入门学习⑦——继承和多态(超级详细版)

    目录 前言 继承 继承是什么? 为什么会存在继承? 语法: 一些基本的定义: 三种继承方式: 对象模型 对于构造和析构的顺序 同名函数的处理方式 总结: 静态成员: 定义: 性质: 共享数据  编译阶段分配内存 类内声明类外初始化 静态成员函数 静态成员函数与普通成员

    2024年02月21日
    浏览(43)
  • C++从入门到精通 第九章(继承和多态)【下】

    (1)一个面向对象的系统常常要求一组具有相同基本语义的方法能在同一接口下为不同的对象服务,这就是多态性。 (2)在C++中,多态性可分为编译时的多态性(静态多态)和运行时的多态性(动态多态),编译时的多态性是通过函数重载和模板体现的,运行时的多态性是

    2024年02月21日
    浏览(46)
  • 【C++】继承和多态、public、private、protected、重写

    继承是一种机制,通过它 一个类可以从另一个类继承属性和方法 。派生类(子类)继承基类(父类)的成员函数和数据成员,并且可以在其基础上扩展自己的成员函数和数据成员。C++支持多重继承,即一个派生类可以同时从多个基类中继承。 多态是指 同一种操作作用于不同

    2024年02月03日
    浏览(52)
  • python中的类class: 继承、覆盖、重写、重载、扩展、多态、封装

    使用  class  创建类。类中有方法、属性。 1.1 __init__() 函数 类的内置  __init__()  函数。所有类都有一个名为 __init__() 的函数,它在启动类时执行。 使用 __init__() 函数将值赋给对象属性,或者在创建对象时需要执行的其他操作。 每次使用类创建新对象时,都会 自动调

    2024年02月08日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包