c++中通过指针或引用实现多态的本质!!

引言:  在c++中司空见惯的事情就是:可以通过指针和引用可以实现多态,而对象不可以。  那为什么?让我们来解开这神秘的暗纱!

1、 类对象的存储方式:

在一个类的实例中,只会存放非静态的成员变量。 如果该类中存在虚函数的话,再多加一个指向虚函数列表指针—vptr。

例如声明如下两个类,并分别实例化两个对象,它们的内存分配大致如下:(vptr具体在什么位置,与编译器有关,大多数都在开始处)

class base
{
public:
    virtual ~base() {};
    virtual string GetName() { return "base"; }
    GetA();
    int a;
};

class derived : public base
{
public:
    virtual ~derived() {};
    virtual string GetName() { return "derived";}
    GetB();
    int b;
};

base B1, B2;derived D1, D2;

内存分布大致如下:

1. 类对象中,只有成员变量与vptr.

2. 普通成员函数在内存的某一位置放着。它们与c语言中定义的普通函数没有区别。 当我们通过对象或对象指针调用普通成员函数时, 编译器会拿到它。怎么拿到呢?当然是通过名字了,编译器都会对我们写的函数的名字进行修饰映射,让它们变成内存中唯一的函数名。

3. 无论基类还是子类,每一种类类型的虚函数表只有一份,它里面存放了基类的类型信息和指向基类中的虚函数的指针。 某一类类型的所有对象都指向了相同的虚函数表。

2.  无论通过对象还是指针,能使用的方法只与它们静态类型有关。

例如:下面的 base类型的对象B1或指针pB1,只能使用GetName() 和GetA()方法。 无论它们是如何来的!!!!!

// 直接构造得到
base B1;
base* pB1 = new base();

// 即使从子类转换而来, 通过B1或pB1也永远访问不到GetB()方法。
derived d1;
B1 = d1;
pB1 = new derived();

3.  不同类型的指针有什么区别?

本质上它们没有任何区别,在32/64位系统中都是4/8字节的一个变量。 唯一不同的就是编译器解释它们的方式,即通过指针来寻址出来的对象类型不同,大小不同 ,指针类型来告诉编译器如何解释该指针。

4. 指针与引用来实现多态

有代码如下 :

derived* _pD = new derived();
base* _pB = _pD;
_pB.GetName();    // 返回 derived.

想要知道如何通过指针来实现的多态,就要看看对基类指针赋值是发生了什么! 具体来说 如下图所示:

我们会发现,对指针的赋值,仅仅是让基类指针_pB指向的子类对象的地地址。 当我们使用基类指针调用GetName()函数(该函数是虚函数,它的地址在函数表中)时, 会由_pB指向的地址找到子类的虚函数表指针vptr_上海,再由vptr_上海在虚函数表中找到子类的GetName(),从而调用它。就这样实现了多态。

5. 对象不能实现多态

有代码如下:

base B1;
derived D1;
B1 = D1;
B1.GetName();     // 返回 base

base B2 = D1
B2.GetName();    // 返回 base

上面代码中无论赋值操作还是赋值构造时, 只会处理成员变量,一个类对象里面的vptr永远不会变,永远都会指向所属类型的虚函数表,操作如下图所示:

因此,通过对象调用虚函数时,就没有必要进行动态解析了,白白增加了间接性,浪费性能。编译器直接在编译时就可以确认具体调用哪一个函数了,因此没有所谓的多态。

补充说明:

1. 引用本质上也是通过指针来实现的,可以<<参考std源码剖析》一本书,所以引用也可以实现多态。

2. 即使通过 基类的指针调用基类的虚函数 或 通过子类的指针调用子类的虚函数 以及通过子类指针调用基类的虚函数,  也是通过多态机制来完成的(即一步步的间接性来完成)。

3.  一个空的class的对象的大小为1个字节, 编译器之所以要这么做,是为了区别同一个类类型的不同对象!

原文地址:https://www.cnblogs.com/yinheyi/p/10525543.html

时间: 2024-07-28 21:25:28

c++中通过指针或引用实现多态的本质!!的相关文章

C++中的指针和引用

此文来自Listening_music:   http://blog.csdn.net/listening_music/article/details/6921608 感谢作者提供的美文,本人很受用,谢谢! 1.指针和引用的定义 下面用通俗易懂的话来概述一下指针和引用: 指针-对于一个类型T,T*就是指向T的指针类型,也即一个T*类型的变量能够保存一个T对象的地址,而类型T是可以加一些限定词的,如const.volatile等等.见下图,所示指针的含义: 引用-引用是一个对象的别名,主要用于函数

#详解C++中的指针与引用

指针和引用形式上很好区别,但是他们似乎有相同的功能,都能够直接引用对象,对其进行直接的操作.但是什么时候使用指针?什么时候使用引用呢?这两者很容易混淆,在此介绍一下指针和引用,力争将最真实的一面展现给大家. 1.指针和引用的定义 在深入介绍之前我们首先来看一下指针和引用的定义.指针和引用的区别,然后分别针对指针和引用展开讨论,深入细节为何有这些差异. 指针的权威定义: In a declaration T D where D has the form * cv-qualifier-seqopt

[Js-c++]c++中的指针、引用和数组名

指针和引用学了好久,还是偶尔会弄混淆,简单记录分析一下区别吧,有记起来新的区别就不断更新添加进去 1.指针可以多次重新分配,引用在无法重新分配,而且必须在声明的时候被初始化 2.指针可以指向空(即被定义为nullptr),但是引用必须指向一个东西 3.不存在"引用计算"这种东西(但是你用 &d+5 这种的确不会报错,也能算出来东西) 4.一个指针在堆栈上有自己的内存地址和大小(x86上有4个字节),而引用共享相同的内存地址(与原始变量),但是也占用堆栈上的一些空间.由于引用具有

malloc与new,C++中的指针与引用,C++struct与class的区别

一.struct与class的区别 1. struct与class的区别不大 在默认情况下,class默认的权限是private(私有的),struct默认的是public(共有的). 2.但是在C++中,struct进行了扩展,现在它已经不仅仅是一个包含不同数据类型的数据结构了,它包括了更多的功能. 二 .malloc与new,delete与free的区别 1.malloc与free是库函数里的函数,调用时要添加头文件,delete与new是C++中的运算符 2.malloc返回值是void*

传指针和传指针引用的区别/指针和引用的区别(本质)

转自:http://blog.sina.com.cn/s/blog_673ef8130100imsp.html 指针传递参数本质上是值传递的方式,它所传递的是一个地址值.值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本.值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值.(这里是在说实参指针本身的地址值不会变) 而在引用传递过程中,被调函数的形式参数虽然也作为局

C++中指针和引用的区别

此文转自:http://www.cnblogs.com/kingln/articles/1129114.html 从概念上讲.指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变. 而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量). 在C++中,指针和引用经常用于函数的参数传递,然而,指针传递参

向函数中传递指针和传递指针的引用的区别

如果是传递指针,那么会先复制该指针,在函数内部使用的是复制后的指针,这个指针与原来的指针指向相同的地址,如果在函数内部将复制后的指针指向了另外的新的对象,那么不会影响原有的指针:但 是对于传递指针应用,如果将传递进来的指针指向了新的对象,那么原始的指针也就指向了新的对象,这样就会造成内存泄漏,因为原来指针指向的地方已经不能再 引用了,即使没有将传递进来的指针指向新的对象,而是在函数结束的时候释放了指针,那么在函数外部就不能再使用原有的指针了,因为原来的内存已经被释放了 看个例子: #includ

指针的引用——在函数中的巧妙应用

前面有一篇文章讲了指针与引用.并且还提到不能定义指向引用的指针(因为引用不是对象,没有实际的地址).那么,能否定义指针的引用呢?答案是肯定的,因为指针本身就是一个对象.即然是对象,就能定义一个引用邦定该对象.先来看看指针的引用的用法: 以int类型为例如下: void ReferencePointer() { int i = 5; // 变量i int* p = &i; // 指针p,指向变量i int*& pr = p; // 指针的引用,与p指向同一个变量i cout <<

(C/C++)区别:数组与指针,指针与引用

1.数组跟指针的区别 数组要么在静态存储区被创建(如全局数组),要么在栈上被创建.数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变. 指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存.指针远比数组灵活,但也更危险. 数组和指针特点的简单比较: 数组 指针 保存数据 保存地址 直接访问数据 间接访问数据,先取得指针的内容,然后以它为地址取得数据 用于存储数目固定且类型相同的数据 通常用于动态数据结构 编译器自动分配和删除