this指针和虚函数理解

编程入门 行业动态 更新时间:2024-10-10 16:24:10

this<a href=https://www.elefans.com/category/jswz/34/1768268.html style=指针和虚函数理解"/>

this指针和虚函数理解

        • this指针
        • vptr和vtbl
        • 小技巧
        • 示例加注释

this指针

就是对象的首地址.
无虚函数时,即第1个成员变量的地址.
有虚函数,第1个成员变量为修正为vptr,指向vtbl
调用成员函数时,如函数里要使用成员变量,编译器根据this指针的偏移来寻访各成员变量的值.
这就是成员变量or函数都隐含this的含义.

vptr和vtbl

vptr指向虚函数表的指针
在构造对象完成后才产生.vptr将指向一个虚函数表,每个表对应类继承的虚函数实现,如果重写了,则是自己的虚函数实现.
多继承,可能出现多个表,如果自己还有虚函数,则排列在首个vptr表的后面
vtbl则是在编译期间,编译器为有虚函数的类生成.
继承关系复杂了,虚函数多了,虚函数表也好,查表也好,显然都会成为负担.不知业界如何解决的.

小技巧

用gdb查看vtbl.

一个包含多继承的对象

(gdb) p dd$17 = (Derived_2) {<Base<char>> = {_vptr.Base = 0x4014b0 <vtable for Derived_2+16>, m_b = 4, a = "\000\000\000"}, <Base<int>> = {_vptr.Base = 0x4014d0 <vtable for Derived_2+48>, m_b = 5, a = {0, 255, 0, 0}}, m_d = 6}

查看表:

(gdb) print /a *((void**)0x4014b0)@10$45 = {0x401136 <Derived_2::fun_virtual()>, 0x401168 <Derived_2::new_fun_virtual()>, 0xfffffffffffffff0, 0x401520 <_ZTI9Derived_2>, 0x401161 <_ZThn16_N9Derived_211fun_virtualEv>, 0x0, 0x401568 <_ZTI4BaseIiE>, 0x40130c <Base<int>::fun_virtual()>, 0x0, 0x401588 <_ZTI7Derived>}

中间还有些奇奇怪怪的地址不知道是什么.

示例加注释
#include <iostream>
#include <string>
#include <assert.h>using std::cout;
using std::endl;template <typename T>
class Base {public:Base(){};Base(int b): m_b(b){};~Base(){};void fun_base() {std::cout << "base" << std::endl;};virtual void fun_virtual() {std::cout << "base virtual" << std::endl;};int getVal() {cout << __func__ << endl; return m_b;};void setVal(int val) {m_b = val;};void print_this(){ cout << "This: " << this << endl;}int m_b;T a[4];
};class Derived : public Base<char> {public:Derived(){};Derived(int b, int d):Base<char>(b), m_d(d) {};~Derived(){};void fun_derived() {std::cout << "derived" << std::endl;};void fun_virtual() {std::cout << "derived virtual" << std::endl;};int getVal() {return m_d;};int m_d;
};class Derived_2 : public Base<char>,public Base<int> {public:Derived_2(){};Derived_2(int cb, int cd, int d):Base<char>(cb),Base<int>(cd),m_d(d) {};~Derived_2(){};void fun_derived() {std::cout << "derived_2" << std::endl;};void fun_virtual() {std::cout << "derived_2 virtual" << std::endl;};int getVal() {return m_d;};virtual void new_fun_virtual(){std::cout << "derived_2 virtual" << std::endl;};int m_d;
};int main(int argc, char* argv)
{Base<char> b(1);Derived d(2,3);Derived_2 dd(4,5,6);/**定义了虚函数的对象,首元素为vptr*/cout <<  "sizeof  int: " << sizeof(int) << endl;//4 + 4 + 8(vptr)cout << "sizeof b :"  << sizeof(b) << endl;// 4+4+4+8 = 20 再8字节对齐 = 24cout << "sizeof d :"  << sizeof(d) << endl;/** 成员函数调用通过this偏移寻访其他成员变量* 普通函数调用fun(a,b,c,d);将d,c push stack,a b可能直接寄存器* 成员函数也是同样的!只不过d,c不明显.这里包含了一种面象过程到面向对象转变的深刻思想*/b.fun_base();b.fun_virtual();std::cout << b.getVal() << std::endl;/** bb仍然可以调用Base::func! 只不过push的this不知道是什么东西了* */Base<char>*  bb =  nullptr;bb->fun_base();//std::cout << bb->getVal() <<  std::endl;//不能用/**将子对象d强制转换为父对象b1,实际是通过拷贝构造函数,因此跟vptr无关,不会覆盖它*b=d前 内存视图*b的内存试图*[0x....0] vptrb = ----->*[0x....8] m_b = 1*//**子类有虚函数实现, vptr为指针,占对象首8字节*d的内存视图*[0x....0] vptrd---------->*[0x....8] m_b = 2*[0x....c] m_d = 3*//**b=d后 内存视图*b的内存试图*[0x....0] vptrb = ----->*[0x....8] m_b = 2*/Base<char> b1  = d;/** if no虚函数 this 地址= 1st元素首地址 = 对象取地址* if has虚函数 this 地址= vptr 地址 = 对象取地址* */cout << "Addr of d:  " << &d << endl;d.print_this();cout << "Addr of 1st elem: " <<&d.m_b << endl;cout << "Addr of b:  " << &b << endl;b.print_this();cout << "Addr of 1st elem: " <<&b.m_b << endl;//调用成员函数时一定是Base::func,编译器都判断好了b1.fun_base();//虚函数,从vtbl里找,只有Base::fun_virtual();b1.fun_virtual(); //拷贝构造过来的 来自dcout << "copy from d: " << b1.getVal() << endl; //该值来自d!//Derived d1  = b;//不允许将父类强转为子类//初始化Base<char>* ptr=  &b; //ptr指向d, 但内存模型仍然为Base<char> ptr=  &d; //非虚函数,Base::func_base();ptr->fun_base();//但虚函数显然使用的是实际对象d的vptrptr->fun_virtual();cout << "from d: " << ptr->getVal() << endl; //该值来自d!//引用效果和指针一样Base<char> & ref = d;cout << "Ref virtual call same with pointer\n";ref.fun_virtual();//1 多继承,多个vtbl//用哪个vtbl,看左边指针是什么原型//2 继承者又定义了新的virtual函数,应该合并到第1张vtbl表里,而且排在已有的//虚函数后面Base<char>* ptr_char=  &dd; ptr_char->fun_virtual();return 0;
}

更多推荐

this指针和虚函数理解

本文发布于:2024-02-07 10:24:42,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1756547.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:指针   函数

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!