面向对象的三个基本特征

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

封装

把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

继承

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

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

1. 实现继承是指使用基类的属性和方法而无需额外编码的能力;

2. 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;

3. 可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。

多态

多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,面向对象的核心,多态的目的则是为了接口重用。

 最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。

要熟悉掌握多态,就要了解是动态绑定还是静态绑定

先熟悉几个名次

  1. 静态类型:对象在声明时采用的类型,在编译期既已确定;
  2. 动态类型:通常是指一个指针或引用目前所指对象的类型,是在运行期决定的;
  3. 静态绑定:绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期;
  4. 动态绑定:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期;
class Base { ... };
class Derived: public Base { ... };
Derived d;
Base *pb = &d;             // implicitly convert Derived* => Base*
This example demonstrates that a single object (e.g., an object of type Derived) might have more than one address (e.g., its address when pointed to by aBase* pointer and its address when pointed to by a Derived* pointer).
That can't happen in C. It can't happen in Java. It can't happen in C#. It does happen in C++. In fact, when multiple inheritance is in use, it happens virtually all the time, but it can happen under single inheritance, too.
Reference: <<Effective C++>>

从上面的定义也可以看出,非虚函数一般都是静态绑定,而虚函数都是动态绑定(如此才可实现多态性)

#include <iostream>
using namespace std;

class Father
{
    public:
        virtual void show1()
        {
            cout<<"this is Father virtual function"<<endl;
        }
        void show2()
        {
            cout<<"this is Father function"<<endl;
        }
        void show3()
        {
            cout<<"this is Father not inherit function"<<endl;
        }
};

class Son:public Father
{
    public:
        void show1()
        {
            cout<<"this is Son virtual function"<<endl;
        }
        void show2()
        {
            cout<<"this is Son function"<<endl;
        }

};

int main()
{
    Father *f=new Father();//f的静态类型是Father*,动态类型也是Father*

    Son *s=new Son();//s的静态类型是Son*,动态类型也是Son*

    Father *f1=s;//f1的静态类型是Father,动态类型是f1所指的对象s的类型

    Son *s_null=NULL;//s_null的静态类型是s1,无动态类型 

    f1->show1();//virtual函数动态绑定,运行时根据函数所依赖的对象类型绑定
    f1->show2();//因为show()不是virtual函数,在编译时绑定,无论f1指向谁,静态类型永远是Father
                //即使f1指向s,也调用自己的show()2 

    s->show1();//virtual函数,动态绑定,根据函数依赖的对象的类型,所以调用自己的show1()
    s->show2();//s的动静态类型都是Son*,所以调用自己的show()
    s->show3();/*
                *Son的内存空间存储情况为[Faher成员变量/函数][Son成员变量/函数]+[指向虚表的指针]
                *Son中不包含任何show3()成员函数的信息,所以到基类中寻找           *
                */

    //s_null->show1();崩溃,因为virtual是运行时才能确定,但是依赖的对象类型是NULL,程序崩溃
    s_null->show2();//没virtual,静态绑定,用对象声明时的类型,
    s_null->show3();//同上

    ;
}

如果show1()不是virtual函数,那么不论f1和s指向什么,对show1()的调用在编译时确定

同样的空指针也能够直接调用no-virtual函数而不报错(这也说明一定要做空指针检查啊!),因此静态绑定不能实现多态;

静态绑定和动态绑定的区别:
1. 静态绑定发生在编译期,动态绑定发生在运行期

2. 对象的动态类型可以更改,但是静态类型无法更改;

3. 要想实现动态,必须使用动态绑定

4. 在继承体系中只有虚函数使用的是动态绑定,其他的全部是静态绑定

建议:

  绝对不要重新定义继承而来的非虚(non-virtual)函数(《Effective C++ 第三版》条款36),因为这样导致函数调用由对象声明时的静态类型确定了,而和对象本身脱离了关系,没有多态,也这将给程序留下不可预知的隐患和莫名其妙的BUG;

动态绑定也即在virtual函数中,要注意默认参数的使用。当缺省参数和virtual函数一起使用的时候一定要谨慎,不然出了问题怕是很难排查

class E
{
public:
    )
    {
        std::cout << "E::func()\t"<< i <<"\n";
    }
};
class F : public E
{
public:
    )
    {
        std::cout << "F::func()\t" << i <<"\n";
    }
};

void test2()
{
    F* pf = new F();
    E* pe = pf;
    pf->func(); //F::func() 1  正常,就该如此;
    pe->func(); //F::func() 0  调用了子类的函数,却使用了基类中参数的默认值!
}

请看《Effective C++ 第三版》 条款37。
建议:
绝对不要重新定义一个继承而来的virtual函数的缺省参数值,因为缺省参数值都是静态绑定(为了执行效率),而virtual函数却是动态绑定

参考:http://www.cnblogs.com/lizhenghn/p/3657717.html

https://www.zhihu.com/question/25572937

https://blog.csdn.net/hackbuteer1/article/details/7475622

最新文章

  1. iOS-性能优化3
  2. Android下常见动画
  3. 纯JSP实现简单登录跳转
  4. Linux vim 底下显示行号
  5. 初学java之大数处理
  6. Linux上的运行的jar包
  7. Linux经久不衰的应用程序
  8. 【学习笔记01】:hover为DIV添加鼠标悬停时改变颜色的效果
  9. php使用check box
  10. Eclipse TypeScript 安装
  11. 每天一个Linux命令(16)--which命令
  12. Windows下ActiveMQ的下载和启动
  13. 论文笔记:Auto-DeepLab: Hierarchical Neural Architecture Search for Semantic Image Segmentation
  14. HDU6187(对偶图生成树)
  15. sqlserver 数据简单查询
  16. saliency map [转]
  17. vue axios请求/响应拦截器
  18. 修改Nginx的header伪装服务器
  19. 《Wrox.Professional.Hadoop.Solutions》中文目录全稿
  20. 原来javascript 自带 encodeURI 和 decodeURI文 方法了

热门文章

  1. Kotlin------函数和代码注释
  2. Oracle like &#39;%...%&#39; 优化
  3. HDU 4745 Two Rabbits ★(最长回文子序列:区间DP)
  4. js排序算法05——快速排序
  5. Java复习5.面向对象
  6. 【网络编程】inet_addr、inet_ntoa、inet_aton、inet_ntop和inet_pton区分
  7. MySQL20个经典面试题
  8. C++面向对象高级编程(三)基础篇
  9. shell脚本实例一
  10. 软件测试模型---V模型、W模型、H模型、X模型