C++中为什么要用虚函数、指针或引用才能实现多态?
原文链接:http://blog.csdn.net/zoopang/article/details/14071779
学过C++的都知道,要实现C++的多态性必须要用到虚函数,并且还要使用引用或者指针,以前学习的时候书本上也是这么说,
但是书本上没有说为什么? 其实只要你认真思考过这个问题你会有三个疑问:
为什么要用虚函数?
为什么要用指针或者引用?
为什么使用派生类和基类对象之间直接赋值不能实现??
一个简单的例子:
- class A
- {
- public:
- virtual void print()
- {cout<<"A"<<endl;}
- };
- class B:public A
- {
- public:
- void print()
- {cout<<"B"<<endl;}
- };
- int main()
- {
- A a;
- B b;
- A *pa = &b;//能实现多态
- pa->print();
- a = b; //不能实现多态,为什么?
- a.print();
- return 0;
- }
进一步的,了解C++的人都应该知道只要有虚函数的类就会有一张虚函数表,多态就是通过这张表来实现的。
所以,只要你不断探索下去,就会很快发现前面两个疑问豁然开朗,在这里,我主要探索第三个疑问。
好了,我这里假设你已经对前面两个问题很清楚了。我们进一步把这个问题细化:
程序执行a = b操作时,b中的虚表到底有没有赋给a?
若赋给a了,是不是一定要用指针或引用才能通过虚表调用虚函数?
下面我们通过一个小程序,探索一下:
- class A
- {
- public:
- virtual void T(){}
- virtual void print()
- {cout<<"A"<<endl;}
- };
- class B:public A
- {
- public:
- void print()
- {cout<<"B"<<endl;}
- };
- int main()
- {
- A a;
- B b;
- a = b;
- a.print(); //通过对象直接访问
- b.print();
- return 0;
首先我们通过调试来看看虚函数表:
程序运行到a=b处时:
我们可以看到B类(派生类)如果覆盖了A类(被继承类)的成员函数,则B类(派生类)的虚函数表第二项(*vtptr[1])的内容也会被覆盖,但是A类(被继承类)的虚函数表的其他表项依然被继承过来,表项的顺序和他们在A类(被继承类)的声明是一致(在这里我不再验证)。
程序运行到a.print()处时:
我们可以看到A类(被继承类)中的虚函数表项并没有被覆盖,也就说没有赋值过来,所以自然的使用a.print()时,也只是调用自己的成员函数。
到这里我们找到了第一个疑问的答案。
既然默认的赋值运算没有实现虚表的赋值,那么我们就重写A类的成员函数operaotor =:
- A& operator = (const B& b)
- {
- *(int *)this=*(int *)&b;
- return *this;
- }
添加了代码之后,我们继续来运行上面的代码,得到如图的运行结果:
这说明,对象去访问成员的虚函数并不通过虚函数表。
为了验证虚函数表的确传了过去,可以再添加下面两行代码:
- A *pa = &a;
- pa->print();
输出如下:
到这里为止,提出的疑问基本上已经有了答案。但是,又产生了新的疑问,对象为什么访问不了虚函数表?
一句话解释:
1.默认的赋值运算符并不会操作虚函数表。
2.要实现多态,必须使用指针或者引用。
后话:文章到此结束,对于后面那个疑问,博主我也不知道答案,如果你知道,麻烦留言告知一下,还有就是此文我只是想记录一下我探索的过程,其中可能会有错误,所以如果你发现了错误,请你一定要提出来,以免我误导其他读者,谢谢!
C++中为什么要用虚函数、指针或引用才能实现多态?的更多相关文章
- 为什么C++中只有指针和引用才能实现多态?
代码: class A { public: virtual void Debug(){} }; class B:public A { public: virtual void Debug(){} }; ...
- 由剑指offer引发的思考——对象中虚函数指针的大小
先看一个简单的问题: 一.定义一个空的类型,对于其对象我们sizeof其大小,是1字节.因为我们定义一个类型,编译器必须为其分配空间,具体分配多少是编译器决定,vs是1字节,分配在栈区. 那,这一个字 ...
- C++ 类的多态三(多态的原理--虚函数指针--子类虚函数指针初始化)
//多态的原理--虚函数指针--子类虚函数指针初始化 #include<iostream> using namespace std; /* 多态的实现原理(有自己猜想部分) 基础知识: 类 ...
- 转 C++中不能声明为虚函数的有哪些函数
传送门 C++中不能声明为虚函数的有哪些函数 常见的不不能声明为虚函数的有:普通函数(非成员函数):静态成员函数:内联成员函数:构造函数:友元函数. 1.为什么C++不支持普通函数为虚函数? 普通函数 ...
- C++中的继承与虚函数各种概念
虚继承与一般继承 虚继承和一般的继承不同,一般的继承,在目前大多数的C++编译器实现的对象模型中,派生类对象会直接包含基类对象的字段.而虚继承的情况,派生类对象不会直接包含基类对象的字段,而是通过一个 ...
- 【转载】 C++多继承中重写不同基类中相同原型的虚函数
本篇随笔为转载,原文地址:C++多继承中重写不同基类中相同原型的虚函数. 在C++多继承体系当中,在派生类中可以重写不同基类中的虚函数.下面就是一个例子: class CBaseA { public: ...
- C++中不能声明为虚函数的有哪些函数
常见的不不能声明为虚函数的有:普通函数(非成员函数):静态成员函数:内联成员函数:构造函数:友元函数. 1.为什么C++不支持普通函数为虚函数? 普通函数(非成员函数)只能被overload,不能被o ...
- 虚函数指针sizeof不为sizeof(void*)
ref:http://bbs.csdn.net/topics/360249561 一个继承了两个虚基类又增加了自己的一个虚函数pif的类,sizeof(指向pif的指针)竟然是8(X86).我是从这里 ...
- C++中构造函数能调用虚函数吗?(答案是语法可以,输出错误),但Java里居然可以
环境:XPSP3 VS2005 今天黑总给应聘者出了一个在C++的构造函数中调用虚函数的问题,具体的题目要比标题复杂,大体情况可以看如下的代码: class Base { public: Base() ...
随机推荐
- smarty半小时快速上手入门教程
http://www.jb51.net/article/56754.htm http://www.yiibai.com/smarty/smarty_functions.html http://www. ...
- Git和CocoaPods的简单使用
Git是一款免费.开源的分布式版本控制系统,还有一种SVN的开源的集中式版本控制系统.分布式相比于集中式的最大区别在于开发者可以提交到本地,每个开发者通过克隆(git clone),在本地机器上拷贝一 ...
- From MSI to WiX, Part 2 - ARP support, by Alex Shevchuk
Following content is directly reprinted from From MSI to WiX, Part 2 - ARP support Author: Alex Shev ...
- C#语法需要注意的地方
笔试中遇到的一些C#语法方面的问题,由于平时很少用到,笔试的时候模棱两可,现在做一个笔记. using System; using System.Collections.Generic; using ...
- iscsiadm用法简介
已知192.168.14.112节点,存在目标器 iqn.2015.06.cn.hrbyg.www.ygcs.c0a802b8:wzg,未设置CHAP,存在目标器 iqn.2015.06.cn.hrb ...
- Qt多文档界面应用设计
使用Qt编写多文档界面(MDI)应用相当方便,主要会使用到QMdiArea和QMdiSubWindow两个类.可以查看Qt Asistant中这两个类的说明文档,里面介绍的相当详细.另外,可以搜索例程 ...
- RX编程笔记——JavaScript 获取地理位置
RX编程笔记——JavaScript 获取地理位置 2016-07-05
- AD,Group
DataTable dtUser = GetEmptyDT(); Dictionary<DirectoryEntry, string> test1 = GetUserAndGroup(cl ...
- DataGridView 绑定数据后修改列类型
dataGridView1.DataSource = dt; dataGridView1.Columns.RemoveAt(); //删除要被替换的列 DataGridViewLinkColumn l ...
- DOM五大对象
1.Window 对象:Window 对象表示浏览器中打开的窗口. 如果文档包含框架(frame 或 iframe 标签),浏览器会为 HTML 文档创建一个 window 对象,并为每个框架创建一个 ...