IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++虚函数 -> 正文阅读

[C++知识库]C++虚函数

虚函数和多态

https://www.bilibili.com/video/BV1LK411s7ES

首先明确一个空类产生的对象的大小为1B:

class A {
};

int main() {
    A a;
    cout << sizeof(a) << endl;		//1
}

然后我们向类A中加入两个普通成员函数,对象的大小还是1B:

class A {
public:
    void func1() {}
    void func2() {}
};

int main() {
    A a;
    cout << sizeof(a) << endl;		//1
}

然而一旦我们向A中加入一个虚函数,其对象的大小变为8B:

class A {
public:
    void func1() {}
    void func2() {}
    virtual void vfunc() {}
};

int main() {
    A a;
    cout << sizeof(a) << endl;		//8
}

原因:当引入虚函数后,编译器在类中插入了一个虚函数表指针vptr,类似于下面的伪码:

class A{
public:
	void* vptr;
	...
};

而vtpr是占用类对象的内存空间的:

(gdb) p a
$1 = (A) {_vptr.A = 0x7ff771b64520 <vtable for A+16>}

每个类对象的虚函数表指针vptr指向这个类的虚函数表vtbl,编译器在编译期间在类A的构造函数内安插vptr的赋值语句:

class A {
public:
	A() {
		vptr = &A::vtbl;	//编译器做的
		...
	}
	void* vptr;
};

考虑如下的A类对象的内存布局:

class A {
public:
    void func1() {}
    void func2() {}
    virtual void vfunc() {}
    virtual void vfunc2() {}
    virtual ~A() {}
private:
	int m_a;
	int m_b;
};

在这里插入图片描述

(gdb) p a
$1 = (A) {_vptr.A = 0x7ff77a105520 <vtable for A+16>, m_a = 0, m_b = 1}

虚析构的作用

注意上面的例子中把A的析构函数设为虚函数,在实际开发中,这样做的目的是保证当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用,否则其只会调用基类的析构函数,如果派生类中有指针成员持有堆区内存,就得不到释放而造成内存泄漏

例如,这是正确的形式:

#include <iostream>
using namespace std;

class Base {
public:
    virtual ~Base() {
        cout << "Base dtor" << endl;
    }
};

class Derived : public Base {
public:
    ~Derived() {
        cout << "Derived dtor" << endl;
    }

};

int main () {
    Base* pb = new Derived();
    delete pb;
    return 0;
}

当delete父类指针时,子类的dtor也一同被调用:

daniel@ubuntu:~/project/demo$ g++ virtual-dtor.cpp 
daniel@ubuntu:~/project/demo$ ./a.out 
Derived dtor
Base dtor

如果去掉父类dtor前的virtual,则只会调用子类的dtor:

#include <iostream>
using namespace std;

class Base {
public:
    ~Base() {
        cout << "Base dtor" << endl;
    }
};

class Derived : public Base {
public:
    ~Derived() {
        cout << "Derived dtor" << endl;
    }

};

int main () {
    Base* pb = new Derived();
    delete pb;
    return 0;
}
daniel@ubuntu:~/project/demo$ g++ virtual-dtor.cpp 
daniel@ubuntu:~/project/demo$ ./a.out 
Base dtor

多态:当通过父类指针指向子类对象,或通过父类引用绑定子类对象,调用父类中的虚函数,实际调用的是对应子类的虚函数

在这里插入图片描述

class Base {
public:
    virtual void myvirfunc() {}
};

int main() {
    Base* pb = new Base();
    pb->vfunc();	//this is polymorphic

    Base b;
    b.vfunc();		//this is not polymorphic

    Base* pb2 = &b;
    pb2->vfunc();	//this is polymorphic
}

多态的表现:

在这里插入图片描述

class Base {
public:
    virtual void myvirfunc() {}
};

class Derive : public Base {
public:
    virtual void myvirfunc() {}
};

int main() {
    //父类指针指向子类对象
    Derive d;
    Base* pb = &d;
    pb->myvirfunc();

    Base* pb2 = new Derive();
    pb2->myvirfunc();

    //父类引用绑定子类对象
    Derive d2;
    Base& rb = d2;
    rb.myvirfunc();
}

存在继承关系时虚函数表指针指向:

设父类有f(), g(), h()这三个虚函数,子类重写了父类中的g()虚函数,则父子类对象的虚函数表指针和虚函数表如下:

在这里插入图片描述

延申思考题:

  1. 当进行多重继承时,子类对象有几个虚函数表指针?子类对象有几个虚函数表?
  2. 虚基类表指针在对象内存中的布局?

用C语言模拟多态

https://www.bilibili.com/video/BV15g4y1a7F3

对于下面的多态实例:

#include <iostream>
using namespace std;

class ISpeaker {
protected:
    int b;
public:
    ISpeaker (int bb) : b(bb) {}
    virtual void speak() = 0;
};

class Dog : public ISpeaker {
public:
    Dog() : ISpeaker(0) {}
    virtual void speak() override {
        printf("woof %d\n", b);
    }
};

class Human : public ISpeaker {
private:
    int c;
public:
    Human() : ISpeaker(1), c(2) {}
    virtual void speak() override {
        printf("hello %d\n", c);
    }
};

int main(){
    ISpeaker* d = new Dog();
    ISpeaker* h = new Human();

    d->speak();
    h->speak();

    return 0;
}

在这里插入图片描述
其用C语言可以描述如下:

#include <iostream>
using namespace std;

extern "C" {
	//虚函数表类型
    struct vft {
        void (*speak) (void* ptr);
    };
	//Dog的speak方法
   void Dog_speak (void* ptr) {
        void* p = ptr + sizeof(vft*);
        int bb = *((int*)p);
        printf("woof %d\n", bb);
    }
	//Human的speak方法
    void Human_speak (void* ptr) {
        void* p = ptr + sizeof(vft) + sizeof(int);
        int cc = *((int*)p);
        printf("hello %d\n", cc);
    }
	//Dog的虚函数表,其speak函数指针指向Dog_seapk
    const static vft Dog_vft= {
        .speak = Dog_speak
    };
	//Human的虚函数表,其speak函数指针指向Human_speak
    const static vft Human_vft = {
        .speak = Human_speak
    };
	//基类型内存模型
    struct ISpeaker {
        const vft* vptr;
        int b;
    };
	//Dog类型内存模型
    struct Dog {
        const vft* vptr;
        int b;
    };
	//Human类型内存模型
    struct Human {
        const vft* vptr;
        int b;
        int c;
    };
 
    Dog* Dog_constructor() {
        Dog* d =(Dog*)malloc(sizeof(Dog));
        d->vptr = &Dog_vft;
        d->b = 0;
        return d;
    }

    Human* Human_constructor() {
        Human* h =(Human*)malloc(sizeof(Human));
        h->vptr = &Human_vft;
        h->b = 1;
        h->c = 2;
        return h;
    }   

    int main () {
        ISpeaker* s1 = (ISpeaker*)Dog_constructor();
        ISpeaker* s2 = (ISpeaker*)Human_constructor();
        
        s1->vptr->speak(s1);
        s2->vptr->speak(s2);
        return 0;
    }
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-04 00:51:43  更:2022-09-04 00:54:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 7:31:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码