文档章节

探究 C++ 中多态、虚函数、虚函数表与 this 的关系

傅易
 傅易
发布于 2017/03/28 14:58
字数 886
阅读 174
收藏 2
C++

最近因为同学的一个问题,我研究了一下 C++ 的多态性。

先讲讲什么是重载(overload)、覆写(override)和多态(polymorphism)。

重载是:同一个方法名,不同入参对应不同函数,实际是不同的方法签名

覆写是:方法名和入参都相同,即方法签名相同,却对应不同函数。一般允许子类继承父类,覆写父类方法。

多态是:通过父类的方法签名,实际访问到了子类覆写的方法,这一现象叫多态。

C++ 支持静态联编的编译时多态,和动态联编的运行时多态。函数联编是指对一个函数的调用,是确定“函数引用的目标函数体”的过程,可以理解为确定 foo() 这里 foo 指向的真实函数体地址的过程。

C++ 的动态联编通过虚函数(virtual method / virtual function)实现。虚函数存在于继承关系中,在基类声明虚函数,子类覆写虚函数,使我们能够通过表面使用基类的该函数,实际访问到子类覆写后的函数。

#include <iostream>
#include <string>

using namespace std;

class A {
public:
    A(){}
    virtual void foo()=0; // 纯虚函数,声明该函数在 A 中不会被实现
    virtual~A(){}
};

class B : public A {
public:
    B() {}
    virtual void foo();
    virtual ~B(){}
};

void B::foo() {
    cout << "Hello, virtual method." << endl;
}

int main() {
    A* b = new B();
    b->foo(); // Hello, virtual method.

    return 0;
}

注意,必须使用指针访问,如果不通过指针访问会发生对象切片(object slicing)。

关于对象切片参见 StackOverflow: Why do virtual functions need to be passed with a pointer and not by value(of the object) ?

C++ 中虚函数是通过虚函数表(virtual table,v-table)来实现的。

每个有虚函数的类有一个虚函数表,包括纯虚函数和派生类中隐式声明的虚函数。虚函数表的入口指针在对象最开始的位置。虚函数表只存储虚函数“函数指针”的地址,不存放普通函数或是构造函数指针的地址。

//TODO 虚函数的内存分析这里不写了,看到前辈总结的已经很好,留个链接:

CSDN: C++虚函数表详细解释及实例分析

看下面的例子:

#include <iostream>
#include <string>

using namespace std;

class A {
public:
    A(){}
    virtual void foo()=0;
    virtual~A(){}
};

class B : public A {
public:
    B() {}
    virtual void foo();
    virtual ~B(){}
};

void B::foo() {
    cout << "Hello, virtual method." << endl;
}

typedef void (*func)(A*);

int main() {
    A* b = new B();

    void*** vtable = (void***)(b);
    cout << "虚函数表的地址 " << table << endl;
    void** func1 = (void**)(*table + 0);
    cout << "第一个虚函数的地址" << func1 << endl;
    func p = (func)(*func1);
    p(); // Hello, virtual method.

    return 0;
}

其实上面的实例有问题,C++ 的对象中 this 的实现是在编译时改写原方法,在首位增加参数 _this,是指针指向对象。

可以理解为(但实际签名不是这样):

void B_foo(B* _this) {
    cout << "Hello, " << _this->val << endl;
}

int main() {
    A* b = new B();
    B_foo(b);

    return 0;
}

所以,当我们需要在虚函数中使用对象变量的时候,我们就要传入第一个参数:

#include <iostream>
#include <string>

using namespace std;

class A {
public:
    A(){}
    virtual void foo()=0;
    virtual~A(){}
};

class B : public A {
public:
    B(int a = 0) : a(a) {}
    virtual void foo();
    virtual ~B(){}
private:
    int a;
};

void B::foo() {
    cout << "Hello, " << this->a << endl;
}

typedef void (*func)(A*);

int main() {
    A* b = new B();

    void*** vtable = (void***)(b);
    void** func1 = (void**)(*table + 0);
    func p = (func)(*func1);
    p(b); // Hello, 0

    return 0;
}

现在是不是对虚函数、甚至 C++ 对象的实现有了一些认识?祝大家学习愉快。

© 著作权归作者所有

傅易
粉丝 28
博文 111
码字总数 69348
作品 0
海淀
后端工程师
私信 提问
C++ 虚函数机制分析

原文:C++ 虚函数机制分析 作者:Breaker C++ 中的虚函数调用机制通常是靠虚函数表 (vtbl) 和虚表指针 (vptr) 实现的,调用行为称为 晚绑定 (later binding)、动态绑定 (dynamic binding) 或...

晨曦之光
2012/05/23
476
0
C++灵魂所在之---多态的前世与今生

开头先送大家一句话吧: 众所周知,在20世纪80年代早期,C++在贝尔实验室诞生了,这是一门面向对象的语言,但它又不是全新的面向对象的语言,它是在传统的语言(C语言)进行面向对象扩展而来...

loving_forever_
2016/06/13
0
0
C++虚函数表解析

C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态...

长平狐
2012/10/08
50
0
C++学习笔记 -- 虚析构函数与纯虚析构函数

开始学C++了,所以又重拾以前学习过的相关概念… 析构函数是当一个对象的生命周期结束时,会自动执行析构函数。 析构函数的定义: #ifndef AH #define AH class A { public: A(void); A(int...

meteoric
2013/05/08
0
0
C++ 虚函数表解析

原文作者:陈皓 原文地址:C++虚函数表解析 C++ 虚函数表解析 陈皓 http://blog.csdn.net/haoel 前言 C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指...

长平狐
2013/12/25
110
0

没有更多内容

加载失败,请刷新页面

加载更多

Guava RateLimiter + AOP注解实现单机限流、统计QPS

1、基于springboot项目pom.xml添加如下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId></dependency><d......

铁骨铮铮
18分钟前
3
0
龙芯版办公软件下载

金山wps office   rpm包:http://ftp.loongnix.org/os/loongnix/1.0/os/Packages/w/wps-office-10.8.0.6472-1.a20p1.mips64el.rpm   deb包:http://packages.deepin.com/loongson/pool/......

gugudu
23分钟前
2
0
BI报表分析和数据可视化,推荐这三个开源工具!

开源篇 一、Superset 1、技术架构:Python + Flask + React + Redux + SQLAlchemy 2、使用人群: (1)开发/分析人员做好看板,业务人员浏览看板数据 (2)业务人员可自行编辑图表,查看满足...

飓风2000
30分钟前
1
0
CountDownLatch

CountDownLatch的概念 CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。 CountDownLatch能够使一个线程在等待另外一些线程...

少年已不再年少
39分钟前
1
0
centos7 新手阿里云服务器安装mongodb

简介 MongoDB 是一个基于分布式 文件存储的NoSQL数据库 由C++语言编写,运行稳定,性能高 旨在为 WEB 应用提供可扩展的高性能数据存储解决方案 MongoDB特点 模式自由 :可以把不同结构的文档存...

醉雨
49分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部