`
liyx985
  • 浏览: 214130 次
  • 性别: Icon_minigender_1
文章分类
社区版块
存档分类
最新评论

虚函数和虚基类、纯虚函数、抽象类抽象方法 ,动态绑定

 
阅读更多
抽象类与接口紧密相关,它们不能实例化,并且常常部分实现或根本不实现。抽象类和接口之间的一个主要差别是:类可以实现无限个接口,但仅能从一个抽象(或 任何其他类型)类继承。从抽象类派生的类仍可实现接口。可以在创建组件时使用抽象类,因为它们使您得以在某些方法中指定不变级功能,但直到需要该类的特定 实现之后才实现其他方法。抽象类也制定版本,因为如果在派生类中需要附加功能,则可以将其添加到基类而不中断代码。

在实现抽象类时,必须实现该类中的每一个抽象方法,而每个已实现的方法必须和抽象类中指定的方法一样,接收相同数目和类型的参数,具有同样的返回值。
抽象类不能被实例化,也就是不能用new关键字去产生对象
   抽象方法只需声明,而不需实现
   抽象类的子类必须覆盖所有的抽象方法后才能被实例化,否则这个子类还是个抽象类


虚函数联系到多态,多态联系到继承。所以本文中都是在继承层次上做文章。没了继承,什么都没得谈。

下面是对C++的虚函数这玩意儿的理解。

一,  什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你就应该从这里开始)

简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性 是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。下面来看一段简单的代码

class A{

public:

void print(){ cout<<”This is A”<<endl;}

};

class B:public A{

public:

void print(){ cout<<”This is B”<<endl;}

};

int main(){   //为了在以后便于区分,我这段main()代码叫做main1

A a;

B b;

a.print();

b.print();

}

通过class A和class B的print()这个接口,可以看出这两个class因个体的差异而采用了不同的策略,输出的结果也是我们预料中的,分别是This is A和This is B。但这是否真正做到了多态性呢?No,多态还有个关键之处就是一切用指向基类的指针或引用来操作对象。那现在就把main()处的代码改一改。

int main(){   //main2

A a;

B b;

A* p1=&a;

A* p2=&b;

p1->print();

p2->print();

}

运行一下看看结果,哟呵,蓦然回首,结果却是两个This is A。问题来了,p2明明指向的是class B的对象但却是调用的class A的print()函数,这不是我们所期望的结果,那么解决这个问题就需要用到虚函数

class A{

public:

virtual void print(){ cout<<”This is A”<<endl;}  //现在成了虚函数了

};

class B:public A{

public:

void print(){ cout<<”This is B”<<endl;}  //这里需要在前面加上关键字virtual吗?

};

毫无疑问,class A的成员函数print()已经成了虚函数,那么class B的print()成了虚函数了吗?回答是Yes,我们只需在把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。所以, class B的print()也成了虚函数。那么对于在派生类的相应函数前是否需要用virtual关键字修饰,那就是你自己的问题了。

现在重新运行main2的代码,这样输出的结果就是This is A和This is B了。

现在来消化一下,我作个简单的总结,指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。

虚基类:是为了避免多重继承中产生的二义性问题。比如当在多条继承路径上有一个共同的基类,当在多条路径的交汇处可能产生基类的多个实例(副本),为避免这个问题可以使用虚基类,虚基类并不在声明时指出,而是在子类继承时指明是虚继承。
class a{}
class b{}:virtual a

纯虚函数是一种特殊的虚函数,它的一般格式如下:



    class <类名>

    {

        virtual <类型><函数名>(<参数表>)=0;

        …

    };



    在许多情况下,在基类中不能对虚函数给出有意义有实现,而把它说明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。




绑定:

一.定义:

1.方法绑定:一个方法被调用时该方法关联其方法体的过程。
2.静态绑定:在面向过程的中又称为前期绑定在程序编译时进行了绑定,即在还没运行时,就已经加载到内存。
3.动态绑定:在面向过程中称为后期绑定(运行时绑定)在运行时就进行绑定,根据实际情况有选择的进行绑定。


二.优越性:

         动态绑定灵活性相对静态绑定来说要高,因为它在运行之前可以进行选择性的绑定,很多时候优点就是缺点,正是因为选择性的绑定,所以动态绑定的执行效率要低些(因为,绑定对象,还要进行编译)。

三.静态绑定实例:
Java代码 
//父类 
public class Person { 
     
    protected String attribute="人的特性"; 
 

 
//子类 
public class Male extends Person { 
 
    protected String attribute = "男人的特性"; 
 
     

//测试 
 
public class Tester { 
 
public static void main(String[] args) { 
 
        Person p = new Male(); 
 
        System.out.println("" + p.attribute); 
    } 
 
 




输出结果:人的属性

可以看出子类的对象调用到的是父类的成员变量。所以必须明确,动态绑定针对的范畴只是对象的方法。


static 块静态加载:

Java代码 
public class StaticTest { 
 
    static { 
 
        System.out.println("没有主方法我照样执行"); 
    } 



运行结果:
没有主方法我照样执行java.lang.NoSuchMethodError: main
Exception in thread "main" 

java中的变量都是静态绑定的;
构造方法 以及private,static,final类方法的调用都是静态绑定的。

四.动态绑定实例:

Java代码 
//父类 
public class Person { 
   
  public void  show(){ 
     System.out.println("人的特性"); 
  } 

//子类 
public class Male extends Person{ 
 
   public void show(){ 
 
    System.out.println("男人的特性"); 


//测试 
public  class Tester{ 
 
public static void main(String [] args){ 
             
        Person p = new Male(); 
        p.show(); 
    } 



运行结果:男人的特性

上面当创建一个Male类对象,通过继承的关系向上转型立即赋值给Person类的对象,貌似调用的是Person中的show();,然而从输出的结果看调用的却是Male类中的show();这就是动态绑定的结果,它能在运行时找到实际最适合类型的方法进行调用
分享到:
评论

相关推荐

    详解C++纯虚函数与抽象类

    1.虚函数 1.1虚函数简介 虚函数可以毫不夸张的说是C++最重要的特性之一,我们先来看一... 如果定义为虚函数,那么它就是动态绑定的,也就是在派生类中可以被覆盖的,这与静态成员函数的定义(在内存中只有一份拷贝,

    面向对象实验3类的多态

    进一步掌握C++中的继承机制,掌握虚函数的声明、定义及用法; 在理解虚函数实现动态绑定机制的基础上,理解虚...了解引入纯虚函数的必要性和抽象类的“接口”作用;从而全面理解面向对象技术中的“多态性”这一大特性。

    C++Primer视频(高级)下载地址

    32.15章 纯虚函数与抽象类 33.16章 模板与泛型编程 34.16章 类模板 - 顺序队列 35.16章 类模板 - 链式队列 36.16章 函数模板 37.17章 异常处理(一) 38.17章 异常处理(二) 39.17章 异常处理(三) 40....

    Visual C++开发经验技巧宝典(第1章)

    0061 如何利用虚函数实现动态绑定 25 0062 如何利用抽象类进行数据封装 25 0063 数据类型与类对象大小的区别 26 0064 实现类的强制转换 26 1.8 高级函数 27 0065 如何重载操作符 27 0066 如何定义重载...

    Absolute C++中文版(原书第2版)-完美的C++教程,文档中还包含英文版

    15.1.3 抽象类和纯虚函数 448 15.2 指针和虚函数 451 15.2.1 虚函数和扩展类型兼容性 451 15.2.2 向下转换和向上转换 456 15.2.3 C++如何实现虚函数 457 第16章 模板 461 16.1 函数模板 461 16.2 类模板 ...

    Java面试题1.docx

    - 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。 - 继承:继承是从已有类得到继承信息创建新类的过程。...

    C++的那些事:面向对象

     面向对象基于三个基本概念:数据抽象、继承和动态绑定。通过使用数据抽象,我们可以将类的接口与实现分离;使用继承,可以定义相似的类型并对其相似关系建模;使用动态绑定,可以在一定程度上忽略相似类型的区别...

    程序设计教程 陈家骏等编著

    8.3.4 *虚函数动态绑定的实现 281 8.4 多继承 283 8.4.1 多继承的需要性 283 8.4.2 多继承的定义 284 8.4.3 名冲突 285 8.4.4 重复继承――虚基类 286 8.5 类作为模块 287 8.5.1 类模块的组成 287 8.5.2 *Demeter...

    摩托罗拉C++面试题

    15.为什么要引入抽象基类和纯虚函数? 主要目的是为了实现一种接口的效果。 16.介绍一下模板和容器。如何实现?(也许会让你当场举例实现) 模板可以说比较古老了,但是当前的泛型编程实质上就是模板编程。 它体现...

    Visual C++ 2005入门经典.part08.rar (整理并添加所有书签)

    9.6.5 抽象类 9.6.6 间接基类 9.6.7 虚析构函数 9.7 类类型之间的强制转换 9.8 嵌套类 9.9 C++/CLI编程 9.9.1 C++/CLI类的继承 9.9.2 接口类 9.9.3 定义接口类 9.9.4 类和程序集 9.9.5 被指定为new的函数 9.9.6 委托...

    Visual C++ 2005入门经典.part04.rar (整理并添加所有书签)

    9.6.5 抽象类 9.6.6 间接基类 9.6.7 虚析构函数 9.7 类类型之间的强制转换 9.8 嵌套类 9.9 C++/CLI编程 9.9.1 C++/CLI类的继承 9.9.2 接口类 9.9.3 定义接口类 9.9.4 类和程序集 9.9.5 被指定为new的函数 9.9.6 委托...

    Visual C++ 2005入门经典.part07.rar (整理并添加所有书签)

    9.6.5 抽象类 9.6.6 间接基类 9.6.7 虚析构函数 9.7 类类型之间的强制转换 9.8 嵌套类 9.9 C++/CLI编程 9.9.1 C++/CLI类的继承 9.9.2 接口类 9.9.3 定义接口类 9.9.4 类和程序集 9.9.5 被指定为new的函数 9.9.6 委托...

    Visual C++ 2005入门经典.part09.rar (整理并添加所有书签)

    9.6.5 抽象类 9.6.6 间接基类 9.6.7 虚析构函数 9.7 类类型之间的强制转换 9.8 嵌套类 9.9 C++/CLI编程 9.9.1 C++/CLI类的继承 9.9.2 接口类 9.9.3 定义接口类 9.9.4 类和程序集 9.9.5 被指定为new的函数 9.9.6 委托...

    Visual C++ 2005入门经典.part06.rar (整理并添加所有书签)

    9.6.5 抽象类 9.6.6 间接基类 9.6.7 虚析构函数 9.7 类类型之间的强制转换 9.8 嵌套类 9.9 C++/CLI编程 9.9.1 C++/CLI类的继承 9.9.2 接口类 9.9.3 定义接口类 9.9.4 类和程序集 9.9.5 被指定为new的函数 9.9.6 委托...

    Visual C++ 2005入门经典.part05.rar (整理并添加所有书签)

    9.6.5 抽象类 9.6.6 间接基类 9.6.7 虚析构函数 9.7 类类型之间的强制转换 9.8 嵌套类 9.9 C++/CLI编程 9.9.1 C++/CLI类的继承 9.9.2 接口类 9.9.3 定义接口类 9.9.4 类和程序集 9.9.5 被指定为new的函数 9.9.6 委托...

    解析C++中的虚拟函数及其静态类型和动态类型

    虚拟函数是C++语言引入的一个很重要的特性,它提供了“动态绑定”机制,正是这一机制使得继承的语义变得相对明晰。 (1)基类抽象了通用的数据及操作,就数据而言,如果该数据成员在各派生类中都需要用到,那么就...

    C++数据抽象和问题求解(第6版).[美]Frank M. Carrano(带详细书签).pdf

    C2.1 变量的内存分配和方法的前期绑定 114 C2.2 需要解决的问题 115 C2.3 指针与程序的自由存储 116 C2.3.1 释放内存 118 C2.3.2 避免内存泄漏 119 C2.3.3 避免悬挂指针 122 C2.4 虚方法和多态 124 C2.5 ...

    C++ Primer中文版(第5版)李普曼 等著 pdf 1/3

     15.2 定义基类和派生类 527  15.2.1 定义基类 528  15.2.2 定义派生类 529  15.2.3 类型转换与继承 534  15.3 虚函数 536  15.4 抽象基类 540  15.5 访问控制与继承 542  15.6 继承中的类作用域 547  15.7...

    C++Primer(第5版 )中文版(美)李普曼等著.part2.rar

     15.2 定义基类和派生类 527  15.2.1 定义基类 528  15.2.2 定义派生类 529  15.2.3 类型转换与继承 534  15.3 虚函数 536  15.4 抽象基类 540  15.5 访问控制与继承 542  15.6 继承中的类作用域 547  15.7...

    C#语言规范(4.0版本)

    6.1.12 匿名函数转换和方法组转换 113 6.2 显式转换 113 6.2.1 显式数值转换 114 6.2.2 显式枚举转换 115 6.2.3 可以为 null 的显式转换 115 6.2.4 显式引用转换 116 6.2.5 拆箱转换 117 6.2.6 显式动态转换 117 ...

Global site tag (gtag.js) - Google Analytics