面向对象编程语言(C++)复习笔记(4)——类的继承,封装,多态

前言

面向对象的三个基本特征是:继承、封装、多态。其中,

  • 封装可以隐藏实现细节,使得代码模块化;
  • 继承可以扩展已存在的代码模块(类),它们的目的都是为了——代码重用;
  • 多态则是为了实现另一个目的——接口重用;

继承

继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。其继承的过程,就是从一般到特殊的过程。

通过继承创建的新类称为“子类”或“派生类”。被继承的类称为“基类”、“父类”或“超类”。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。

继承的实现方式?

继承概念的实现方式有三类:实现继承接口继承可视继承

  1. 实现继承是指使用基类的属性和方法而无需额外编码的能力;
  2. 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
  3. 可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。

封装

封装可以隐藏实现细节,使得代码模块化;封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。在面向对象编程上可理解为:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏


多态

多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针

多态的实现

实现多态,有二种方式,覆盖重载
覆盖:是指子类重新定义父类的虚函数的做法。
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

分析:

“重载”是指在同一个类中相同的返回类型和方法名,但是参数的个数和类型可以不同

“覆盖\重写”是在不同的类中。

其实,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。

那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关

真正和多态相关的是“覆盖”。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。

结论就是:重载只是一种语言特性,与多态无关,与面向对象也无关!引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚邦定,它就不是多态。”
 

多态是实现是依赖于虚函数来实现的,之所以虚函数可以分清楚当前调用的函数是基类的还是派生类的,主要在于基类和派生类分别有着自己的虚函数表,再调用虚函数时,它们是通过去虚函数表去对应的函数的。

#include<iostream>
using namespace std;

class A
{
public :
     void print()
    {
         cout << "A" << endl;
    }
    virtual void print1()
    {
        cout << "Virtual A" << endl;
    }

};

class B:public A{
public:
    void print() { cout << "B" << endl; }
    virtual void print1() { cout << "Virtual B" << endl; }
};

void fn(A & s)
{
    s.print();
    s.print1();
}
int main()
{

    A a;
    B b;
    fn(a);
    fn(b);
    return 0;
}

结果:

biao@ubuntu:~/test/C++$ ./a.out 
A
Virtual A
A
Virtual B
biao@ubuntu:~/test/C++$ 

我们可以看出在使用了虚函数之后,程序就只当前该调用哪个类的函数了。其实虚函数表的本质就是一种迟后联编的过程,正常编译都是先期联编的,但是当代码遇到了virtual时,就会把它当做迟后联编,但是为了迟后编译,我么就生成了局部变量–虚函数表,这就增大了一些空间上的消耗。(前提是两个函数的返回类型,参数类型,参数个数都得相同,不然就起不到多态的作用)

#include<iostream>
#include<cmath>
using namespace std;

class A
{
public :
    virtual void fun(int x)
    {
        cout << "A:  " << x << endl;
    }
};
class B :public A
{
public :
    virtual void fun(float x)
    {
        cout << "B: " << x << endl;
    }
};

void test(A & x)
{
    int i = 1;
    x.fun(i);
    float a = 2.0;
    x.fun(a);
}


int main()
{
    A a;
    B b;
    test(a);
    test(b);
    return 0;
}

输出结果:

biao@ubuntu:~/test/C++$ ./a.out 
A:  1
A:  2
A:  1
A:  2
biao@ubuntu:~/test/C++$ 

从输出结果可以看出,fun函数并没有起到多态的作用,这里主要是因为基类和派生类的fun在编译时,被看成了两个不同函数,因为他们的参数不同。如果返回的类型不同,也会出现同样的情况,但是有一种特殊的情况,那就是如果基类中虚函数返回一个基类指针或引用,派生类中返回一个派生类的指针或引用,则c++将其视为同名虚函数而进行迟后联编。

使用虚函数的一些限制

(1):只有类成员函数才能声明为虚函数,这是因为虚函数只适用于有继承关系的类对象中。
(2):静态成员函数不能说明为虚函数,因为静态成员函数不受限与某个对象,整个内存中只有一个,所以不会出现混淆的情况
(3):内联函数不可以被继承,因为内联函数是不能子啊运行中动态的确认其位置的。
(4):构造函数不可以被继承。
(5):析构函数可以被继承,而且通常声明为虚函数。

纯虚函数

(1):解释

虚函数是在基类中被声明为virtual,并在派生类中重新定义的成员函数,可实现成员函数的动态重载
纯虚函数的声明有着特殊的语法格式:virtual 返回值类型成员函数名(参数表)=0;

(2):必要性:

在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重载以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。

(3):抽象类的解释

包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。
在C++中,抽象类只能用于被继承而不能直接创建对象的类(Abstract Class)。

#include<iostream>
#include<cmath>
using namespace std;

class A
{
public :
    virtual void fun() = 0;
};
class B :public A
{
public :
    virtual void fun()
    {
        cout << "B: " << endl;

    }
};

int main()
{
    B b;
    b.fun();
    return 0;
}

内容引用自:

c++的继承详解_William-CSDN博客_c++继承

c++的继承详解_William-CSDN博客_c++继承

C++继承(三)通过菱形继承看virtual继承_zyl_1102179268的博客-CSDN博客

C++多态(一)对象类型与虚函数_zyl_1102179268的博客-CSDN博客

C++封装、继承、多态_ruyue_ruyue的专栏-CSDN博客_c++封装继承多态

 

-----------------------------------------------------------分割线-----------------------------------------------------------

新的文章内容和附件工程文件

已更新在博客首页和:

|工|·-·|重|·-·|浩|:liwen01

感恩您的关注,谢谢~

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

li_wen01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值