C++关于指针,继承和多态介绍

目录
  • 指针
  • 指针和继承
  • 指针、继承和多态

指针

我们在书本上学到的指针基本上都是:首先,指针是一个变量;其次,这个变量存储的值是一个地址。这个是对指针的一个基本理解,最近在编程中发现了一些新的东西。

首先,指针不仅仅是一个地址,还存在一个和所指向内容大小相关的值,如下代码:

#include<iostream>

using namespace std;

int main()
{
    int a = 10;
    int *pa = &a;
    cout << "pa: " << pa << endl;
    cout << "pa+1: " << pa + 1 << endl;

    short b = 1;
    short *pb = &b;
    cout << "pb: " << pb << endl;
    cout << "pb+1: " << pb + 1 << endl;

    void *ppa = &a;
    cout << "ppa:" << ppa << endl;
    cout << "ppa+1: " << ppa + 1 << endl;

    return 0;
}

从运行结果可以看出pa+1,地址增加了4个字节;pb+1,地址增加了2个字节;ppa+1,地址增加了2个字节;而使用void指针指向整型变量a,此时ppa+1,地址只增加了1个自己。从这个结果我们可以很明显看出,指针不仅仅是一个存地址的变量,还存在一个和内存分配相关的值。其实进一步说,指针不仅仅是一个地址值,还存放着如何解释内存的规则。(void指针只存放地址,没有解释规则)

指针和继承

众所周知,继承可以实现用父类的指针来指向子类的对象,为什么可以这样用呢?正如上面所说,指针保存地址和解释内存的规则,当你声明一个父类指针,那么你就指明了该指针的解释规则,当你将子类地址传给指针时,你就相当于给了一块内存给这个指针,然后这个指针就可以用它的规则去解释这块内存。根据上面的说法,那么我们可以得出,子类对象中必定存在一块内存,其分配方式和父类对象一模一样(如果把这块内存单独提取出来,它就是一个父类对象),事实也是如此,而且这块“父类对象”一般都是存放在子类对象的最前面,这就解释了子类在构造的时候,一定会先调用父类构造函数。同时,“父类对象”指针只能访问自己的父类成员。那么考虑多继承的情况,多继承的子类,其内存空间中必定存在多个“父类对象”空间,这些父类空间的地址必定是不同的,那么就会造成同一个多继承的子类,用其不同的父类指针指向它,其地址值是不同的,实际测试的确如此:

#include<iostream>
using namespace std;
class A
{
    public:
        int a = 10;
        long int aa = 100;
};

class B
{
    public:
        int b = 20;

};

class C : public A, public B
{
    public:
        int c = 30;
};

int main()
{
    C c;
    A *pA;
    B *pB;
    C *pC;

    pA = &c;
    pB = &c;
    pC = &c;

    cout << "pA: " << pA << endl;
    cout << "pB: " << pB << endl;
    cout << "pC: " << pC << endl;
    cout << "pA.a: " << &(pA->a) << endl;
    cout << "pA.aa: " << &(pA->aa) << endl;
    cout << "pB.b: " << &(pB->b) << endl;
    cout << "pC.c: " << &(pC->c) << endl;

    return 0;
}

其内存布局如图,pA、pB都指向各自的“父类对象”空间起始位置,pA只能访问a、aa,pB只能访问b(至于为什么a是int却占8个字节,这个和内存对齐有关,自行查询)。

指针、继承和多态

C++的继承和多态绕不过的一个东西便是虚指针和虚函数,这里简单说一下:首先,在含有虚函数的类中,会产生一个虚函数表,注意这个虚函数表是从属于类的,不是从属于对象,也就是多个对象共享这个虚函数表。其次,每个类声明的对象都会有一个虚指针,这个虚指针指向类的虚函数表。(这里只是简单提及一下,更多的东西可以自行查询)看过很多网上的东西,都说虚指针是每个对象的第一个数据成员,也就是分配在最开头的地址空间。其实,我觉得这句话不完全正确,因为当多继承中有虚函数时,虚函数表就有多个,虚指针也有多个,这些虚指针不一定全都存在于最开始的地址空间。应该说,这些虚指针存在于继承的父类所管理区域的开头:

#include<iostream>
using namespace std;
class A
{
    public:
        int a = 10;
        long int aa = 10;
        virtual void f()
        {

        }
};

class B
{
    public:
        int b = 20;
        virtual void g()
        {

        }
};

class C : public A, public B
{
    public:
        int c = 30;
        virtual void cc()
        {

        }
};

int main()
{
    A *pA;
    B *pB;
    C c;
    pA = &c;
    pB = &c;
    cout << pA << endl;
    cout << &(pA->a) << endl;
    cout << &(pA->aa) << endl;
    cout << pB << endl;
    cout << &(pB->b) << endl;
    cout << &(c.c) << endl;

    return 0;
}

从上面的结果可以看出,虚指针不一定都是存在最开始的位置,如果硬要说是开始,应该也是相对于“父类对象”区域的开始。

到此这篇关于C++关于指针,继承和多态介绍的文章就介绍到这了,更多相关C++指针 继承 多态内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 一文掌握 C++ 智能指针的使用方法

    目录 一.RAII 与引用计数 二.std::shared_ptr 三.std::unique_ptr 四.std::weak_ptr 五.总结 一.RAII 与引用计数 了解 Objective-C/Swift 的程序员应该知道引用计数的概念.引用计数这种计数是为了防止内存泄露而产生的. 基本想法是对于动态分配的对象,进行引用计数,每当增加一次对同一个对象的引用,那么引用对象的引用计数就会增加一次, 每删除一次引用,引用计数就会减一,当一个对象的引用计数减为零时,就自动删除指向的堆内存. 在传

  • C++中的封装、继承、多态理解

    封装(encapsulation):就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成"类",其中数据和函数都是类的成员.封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,特定的访问权限来使用类的成员.封装可以隐藏实现细节,使得代码模块化. 继承(inheritance):C++通过类派生机制来支持继承.被继承的类型称为基类或超类,新产生的类为派生类或子类.保持已有类的特性而构造新类的过

  • C++多重继承及多态性原理实例详解

    一.多重继承的二义性问题 举例: #include <iostream> using namespace std; class BaseA { public: void fun() { cout << "A.fun" << endl; } }; class BaseB { public: void fun() { cout << "B.fun" << endl; } void tun() { cout &l

  • C++关于指针,继承和多态介绍

    目录 指针 指针和继承 指针.继承和多态 指针 我们在书本上学到的指针基本上都是:首先,指针是一个变量:其次,这个变量存储的值是一个地址.这个是对指针的一个基本理解,最近在编程中发现了一些新的东西. 首先,指针不仅仅是一个地址,还存在一个和所指向内容大小相关的值,如下代码: #include<iostream> using namespace std; int main() { int a = 10; int *pa = &a; cout << "pa: &quo

  • java面向对象继承与多态介绍

    目录 一.概述 二.继承 2.1 继承的概述 2.2 继承机制 2.3 类中属性,方法的继承与覆盖 2.4 super 关键字 三. 多态 总结 一.概述 面向对象程序设计的三大原则是封装性,继承性和多态性.继承性是子类自动共享父类的数据和方法的机制,它是由类的派生功能体现的.继承具有传递性,使得一个类可以继承另一个类的属性和方法,这样通过抽象出共同的属性和方法组件新的类,便于代码的重用.而多态是指不同类型的对象接收相同的消息时产生不同的行为,这里的消息主要是对类成员函数的调用,而不同的行为是指

  • Java中继承、多态、重载和重写介绍

    什么是多态?它的实现机制是什么呢?重载和重写的区别在那里?这就是这一次我们要回顾的四个十分重要的概念:继承.多态.重载和重写. 继承(inheritance) 简单的说,继承就是在一个现有类型的基础上,通过增加新的方法或者重定义已有方法(下面会讲到,这种方式叫重写)的方式,产生一个新的类型.继承是面向对象的三个基本特征--封装.继承.多态的其中之一,我们在使用JAVA时编写的每一个类都是在继承,因为在JAVA语言中,java.lang.Object类是所有类最根本的基类(或者叫父类.超类),如果

  • Python类的继承与多态详细介绍

    目录 概念 类的创建 类的继承 多态的使用 概念 类(Class): 用来描述具有相同的属性和方法的对象的集合. 类变量:类变量在整个实例化的对象中是公用的.类变量定义在类中且在函数体之外.类变量通常不作为实例变量使用. 类有一个名为 __init__() 的特殊方法(构造方法),该方法在类实例化时会自动调用 self:self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类. 类调用 Car.weight 实例化  car01=Car(5) 实例对象调用  car01

  • java 中继承和多态详细介绍

    继承和多态 一.this super关键字 1.this: 可以在构造器中的第一代码中调用本类中的其他构造器.this(参数) 非类方法参数中隐式传入的参数,表示调用当前方法的对象. 2.super: 可以在构造器的第一句代码调用父类的构造器.super(参数). 非静态方法中表示继承的父类对象,可以调用父类方法和属性. 二.方法的覆写: 子类重新实现了和父类一样的方法.访问修饰和异常都必须至少和父类的相同或者更大的范围. 三.方法的重载: 相同的方法的名字不同的参数列表. 四.多态: java

  • C++中继承与多态的基础虚函数类详解

    前言 本文主要给大家介绍了关于C++中继承与多态的基础虚函数类的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 虚函数类 继承中我们经常提到虚拟继承,现在我们来探究这种的虚函数,虚函数类的成员函数前面加virtual关键字,则这个成员函数称为虚函数,不要小看这个虚函数,他可以解决继承中许多棘手的问题,而对于多态那他更重要了,没有它就没有多态,所以这个知识点非常重要,以及后面介绍的虚函数表都极其重要,一定要认真的理解~ 现在开始概念虚函数就又引出一个概念,那就是重写(覆

  • C语言实现C++继承和多态的代码分享

    这个问题主要考察的是C和C++的区别,以及C++中继承和多态的概念. C和C++的区别 C语言是面向过程的语言,而C++是面向对象的过程. 什么是面向对象和面向过程? 面向过程就是分析解决问题的步骤,然后用函数把这些步骤一步一步的进行实现,在使用的时候进行一一调用就行了,注重的是对于过程的分析.面向对象则是把构成问题的事进行分成各个对象,建立对象的目的也不仅仅是完成这一个个步骤,而是描述各个问题在解决的过程中所发生的行为. 面向对象和面向过程的区别? 面向过程的设计方法采用函数来描述数据的操作,

  • C语言模拟实现C++的继承与多态示例

    一.面向过程编程与面向对象编程的区别 众所周知,C语言是一种典型的面向过程编程语言,而C++确实在它的基础上改进的一款面向对象编程语言,那么,面向过程与面向对象到底有什么样的区别呢? [从设计方法角度看] 面向过程程序设计方法采用函数(或过程)来描述对数据的操作,但又将函数与其操作的数据分离开来. 面向对象程序设计方法是将数据和对象的操作封装在一起,作为一个整体来处理. [从维护角度看] 面向过程程序设计以过程为中心,难于维护. 面向对象程序设计以数据为中心,数据相对功能而言,有较强的稳定性,因

  • C语言模式实现C++继承和多态的实例代码

    这个问题主要考察的是C和C++的区别,以及C++中继承和多态的概念. C和C++的区别 C语言是面向过程的语言,而C++是面向对象的过程. 什么是面向对象和面向过程? 面向过程就是分析解决问题的步骤,然后用函数把这些步骤一步一步的进行实现,在使用的时候进行一一调用就行了,注重的是对于过程的分析.面向对象则是把构成问题的事进行分成各个对象,建立对象的目的也不仅仅是完成这一个个步骤,而是描述各个问题在解决的过程中所发生的行为. 面向对象和面向过程的区别? 面向过程的设计方法采用函数来描述数据的操作,

  • Java封装、继承、多态三大特征的理解

    首先先简单的说一下其3大特性的定义: 封装:隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别.将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成"类",其中数据和函数都是类的成员.封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员.封装的基本要求是: 把所有的属性私有化,对每个属性提供getter和setter方法,如果有一个带参的

随机推荐