菜鸟笔记
提升您的技术认知

c 虚函数机制解析-ag真人游戏

概述:虚函数的目的是实现运行时多态,程序在运行时才知道要调用那些函数。这属于泛型技术(包括模板、rtti、虚函数)。

使用:用于父类型指针指向子类的对象时。

虚函数表v-table

类中成员函数在内存中不占空间,这个比较好理解。例如一个汽车类,有成员轮子、方向盘,有成员方法 跑(),我们制造出一辆汽车的时候只有轮子、方向盘占用空间,而汽车会跑并不会占用内存的。

假如声明一个虚函数,汽车带车斗(),具体带什么车斗呢,不晓得,让子类去实现。大卡车类有个7吨位的车斗,小货车类有个2吨位的车斗,三轮车类有个小车斗。父类汽车类中是什么样子的呢,父类中声明虚函数,就是拿一个小本本(指针),记下来所有的虚函数。当然,这个小本本(指针)是占用内存的。

子类中,父类中所有声明的虚函数都会默认认为是虚函数,不管是否有virtual关键字。子类中虚函数覆盖掉父类中的虚函数,当我们用指针调用时就调用的就是子类的函数了。

class base
{
public:
  virtual void f() { cout << "base::f" << endl; }
  virtual void g() { cout << "base::g" << endl; }
  virtual void h() { cout << "base::h" << endl; }
};

在内存中是什么样子的呢。

 

当子类继承这个类时,如果是继承自该基类的虚函数,则会出现覆盖,如果不是继承自基类的虚函数,则跟到虚函数表的后面。如以下的继承关系。

 

子类的中的内存: 

多重继承,无虚函数覆盖: 

子类的实例内存结构:

多重继承,有虚函数覆盖:

 

子类的实例内存结构:

虚函数的问题

一、通过父类型的指针访问子类自己的虚函数

我们知道,子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到base1的虚表中有derive的虚函数,但我们根本不可能使用下面的语句来调用子类的自有虚函数:

base1 *b1 = new derive();

b1->f1(); //编译出错

任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法,所以,这样的程序根本无法编译通过。但在运行时,我们可以通过指针的方式访问虚函数表来达到违反c 语义的行为。(关于这方面的尝试,通过阅读后面附录的代码,相信你可以做到这一点)

二、访问non-public的虚函数

另外,如果父类的虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,所以,我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数,这是很容易做到的。

如:

class base {

private:

virtual void f() { cout << "base::f" << endl; }

};

class derive : public base{

};

typedef void(*fun)(void);

void main() {

derive d;

fun pfun = (fun)*((int*)*(int*)(&d) 0);

pfun();

另外:

c 的虚函数(virtual function)是通过一张虚函数表(virtual table)来实现的。简称为v-table。 在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。

1. 无继承的情况

#include
using namespace std;

class base {
public:
    virtual void f() { cout << "base::f()" << endl; }
    virtual void g() { cout << "base::g()" << endl; }
    virtual void h() { cout << "base::h()" << endl; }
};

int main()
{
    typedef void (*fun)();

    base *b = new base;
    cout << *(int*)(&b) << endl; //虚函数表的地址存放在对象最开始的位置

    fun funf = (fun)(*(int*)*(int*)b);
    fun fung = (fun)(*((int*)*(int*)b 1));
    fun funh = (fun)(*((int*)*(int*)b 2));


    funf();
    fung();
    funh();


    cout << (fun)(*((int*)*(int*)b 3)); // 最后一个位置为0,表明虚函数表的结束

    return 0;
}

 

注意:在上面这个图中,虚函数表中最后一个节点相当于字符串的结束符,其标志了虚函数表的结束,在codeblocks下打印为0。 

2. 继承,无虚函数覆盖的情形

#include
using namespace std;

class base {
public:
    virtual void f() { cout << "base::f()" << endl; }
    virtual void g() { cout << "base::g()" << endl; }
    virtual void h() { cout << "base::h()" << endl; }
};

class derive: public base {
    virtual void f1() { cout << "derive::f1()" << endl; }
    virtual void g1() { cout << "derive::g1()" << endl; }
    virtual void h1() { cout << "derive::h1()" << endl; }
};

int main()
{
    typedef void (*fun)();

    base *b = new derive;
    cout << *(int*)b << endl;
    fun funf = (fun)(*(int*)*(int*)b);
    fun fung = (fun)(*((int*)*(int*)b 1));
    fun funh = (fun)(*((int*)*(int*)b 2));
    fun funf1 = (fun)(*((int*)*(int*)b 3));
    fun fung1 = (fun)(*((int*)*(int*)b 4));
    fun funh1 = (fun)(*((int*)*(int*)b 5));


    funf(); // base::f()
    fung(); // base::g()
    funh(); // base::h()
    funf1(); // derive::f1()
    fung1(); // derive::g1()
    funh1(); // derive::h1()

    cout << (fun)(*((int*)*(int*)b 6));
    return 0;
}

 

从上表可以发现:

1.  虚函数按照其声明顺序放于表中。

2.  父类的虚函数在子类的虚函数前面。 

3. 继承,虚函数覆盖的情形

#include
using namespace std;

class base {
public:
    virtual void f() { cout << "base::f()" << endl; }
    virtual void g() { cout << "base::g()" << endl; }
    virtual void h() { cout << "base::h()" << endl; }
};

class derive: public base {
    virtual void f() { cout << "derive::f()" << endl; }
    virtual void g1() { cout << "derive::g1()" << endl; }
    virtual void h1() { cout << "derive::h1()" << endl; }
};

int main()
{
    typedef void (*fun)();

    base *b = new derive;
    cout << *(int*)b << endl;
    fun funf = (fun)(*(int*)*(int*)b);
    fun fung = (fun)(*((int*)*(int*)b 1));
    fun funh = (fun)(*((int*)*(int*)b 2));
    fun fung1 = (fun)(*((int*)*(int*)b 3));
    fun funh1 = (fun)(*((int*)*(int*)b 4));


    funf(); // derive::f()
    fung(); // base::g()
    funh(); // base::h()
    fung1(); // derive::g1()
    funh1(); // derive::h1()

    cout << (fun)(*((int*)*(int*)b 5));
    return 0;
}

   

从上表可以看出:

 

1.  覆盖的f()函数被放到了虚表中原来父类虚函数的位置。

2.  没有被覆盖的函数依旧。

3.  可通过获取获取成员函数指针来调用成员函数(即使是private类型的),带 来一定安全性的影响。

 

4. 多继承的情形

 

#include
using namespace std;

class base1 {
public:
    virtual void f() { cout << "base1::f()" << endl; }
    virtual void g() { cout << "base1::g()" << endl; }
    virtual void h() { cout << "base1::h()" << endl; }
};

class base2 {
public:
    virtual void f() { cout << "base2::f()" << endl; }
    virtual void g() { cout << "base2::g()" << endl; }
    virtual void h() { cout << "base2::h()" << endl; }
};

class base3 {
public:
    virtual void f() { cout << "base3::f()" << endl; }
    virtual void g() { cout << "base3::g()" << endl; }
    virtual void h() { cout << "base3::h()" << endl; }
};

class derive: public base1,public base2, public base3 {
    virtual void f() { cout << "derive::f()" << endl; }
    virtual void g1() { cout << "derive::g1()" << endl; }
};

int main()
{
    typedef void (*fun)();

    derive d;
    base1 *b1 = &d;
    base2 *b2 = &d;
    base3 *b3 = &d;


    b1->f(); //derive::f()
    b2->f(); //derive::f()
    b3->f(); //derive::f()
    b1->g(); //base1::g()
    b2->g(); //base2::g()
    b3->g(); //base3::g()

    fun b1fun = (fun)(*(int*)*(int*)b1);
    fun b2fun = (fun)(*(int*)*((int*)b1 1));
    fun b3fun = (fun)(*(int*)*((int*)b1 2));

    b1fun(); // derive::f()
    b2fun(); // derive::f()
    b3fun(); // derive::f()

    return 0;
}

 

 

从上表可以看出:

 

1. 每个父类都有自己的虚表。

2. 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)

3. 对于多继承无虚函数覆盖的情况,布局与上图类似(derive的位置对应base)。

,

概述:虚函数的目的是实现运行时多态,程序在运行时才知道要调用那些函数。这属于泛型技术(包括模板、rtti、虚函数)。

使用:用于父类型指针指向子类的对象时。

虚函数表v-table

类中成员函数在内存中不占空间,这个比较好理解。例如一个汽车类,有成员轮子、方向盘,有成员方法 跑(),我们制造出一辆汽车的时候只有轮子、方向盘占用空间,而汽车会跑并不会占用内存的。

假如声明一个虚函数,汽车带车斗(),具体带什么车斗呢,不晓得,让子类去实现。大卡车类有个7吨位的车斗,小货车类有个2吨位的车斗,三轮车类有个小车斗。父类汽车类中是什么样子的呢,父类中声明虚函数,就是拿一个小本本(指针),记下来所有的虚函数。当然,这个小本本(指针)是占用内存的。

子类中,父类中所有声明的虚函数都会默认认为是虚函数,不管是否有virtual关键字。子类中虚函数覆盖掉父类中的虚函数,当我们用指针调用时就调用的就是子类的函数了。

class base
{
public:
  virtual void f() { cout << "base::f" << endl; }
  virtual void g() { cout << "base::g" << endl; }
  virtual void h() { cout << "base::h" << endl; }
};

在内存中是什么样子的呢。

 

当子类继承这个类时,如果是继承自该基类的虚函数,则会出现覆盖,如果不是继承自基类的虚函数,则跟到虚函数表的后面。如以下的继承关系。

 

子类的中的内存: 

多重继承,无虚函数覆盖: 

子类的实例内存结构:

多重继承,有虚函数覆盖:

 

子类的实例内存结构:

虚函数的问题

一、通过父类型的指针访问子类自己的虚函数

我们知道,子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到base1的虚表中有derive的虚函数,但我们根本不可能使用下面的语句来调用子类的自有虚函数:

base1 *b1 = new derive();

b1->f1(); //编译出错

任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法,所以,这样的程序根本无法编译通过。但在运行时,我们可以通过指针的方式访问虚函数表来达到违反c 语义的行为。(关于这方面的尝试,通过阅读后面附录的代码,相信你可以做到这一点)

二、访问non-public的虚函数

另外,如果父类的虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,所以,我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数,这是很容易做到的。

如:

class base {

private:

virtual void f() { cout << "base::f" << endl; }

};

class derive : public base{

};

typedef void(*fun)(void);

void main() {

derive d;

fun pfun = (fun)*((int*)*(int*)(&d) 0);

pfun();

另外:

c 的虚函数(virtual function)是通过一张虚函数表(virtual table)来实现的。简称为v-table。 在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。

1. 无继承的情况

#include
using namespace std;

class base {
public:
    virtual void f() { cout << "base::f()" << endl; }
    virtual void g() { cout << "base::g()" << endl; }
    virtual void h() { cout << "base::h()" << endl; }
};

int main()
{
    typedef void (*fun)();

    base *b = new base;
    cout << *(int*)(&b) << endl; //虚函数表的地址存放在对象最开始的位置

    fun funf = (fun)(*(int*)*(int*)b);
    fun fung = (fun)(*((int*)*(int*)b 1));
    fun funh = (fun)(*((int*)*(int*)b 2));


    funf();
    fung();
    funh();


    cout << (fun)(*((int*)*(int*)b 3)); // 最后一个位置为0,表明虚函数表的结束

    return 0;
}

 

注意:在上面这个图中,虚函数表中最后一个节点相当于字符串的结束符,其标志了虚函数表的结束,在codeblocks下打印为0。 

2. 继承,无虚函数覆盖的情形

#include
using namespace std;

class base {
public:
    virtual void f() { cout << "base::f()" << endl; }
    virtual void g() { cout << "base::g()" << endl; }
    virtual void h() { cout << "base::h()" << endl; }
};

class derive: public base {
    virtual void f1() { cout << "derive::f1()" << endl; }
    virtual void g1() { cout << "derive::g1()" << endl; }
    virtual void h1() { cout << "derive::h1()" << endl; }
};

int main()
{
    typedef void (*fun)();

    base *b = new derive;
    cout << *(int*)b << endl;
    fun funf = (fun)(*(int*)*(int*)b);
    fun fung = (fun)(*((int*)*(int*)b 1));
    fun funh = (fun)(*((int*)*(int*)b 2));
    fun funf1 = (fun)(*((int*)*(int*)b 3));
    fun fung1 = (fun)(*((int*)*(int*)b 4));
    fun funh1 = (fun)(*((int*)*(int*)b 5));


    funf(); // base::f()
    fung(); // base::g()
    funh(); // base::h()
    funf1(); // derive::f1()
    fung1(); // derive::g1()
    funh1(); // derive::h1()

    cout << (fun)(*((int*)*(int*)b 6));
    return 0;
}

 

从上表可以发现:

1.  虚函数按照其声明顺序放于表中。

2.  父类的虚函数在子类的虚函数前面。 

 

 3. 继承,虚函数覆盖的情形

#include
using namespace std;

class base {
public:
    virtual void f() { cout << "base::f()" << endl; }
    virtual void g() { cout << "base::g()" << endl; }
    virtual void h() { cout << "base::h()" << endl; }
};

class derive: public base {
    virtual void f() { cout << "derive::f()" << endl; }
    virtual void g1() { cout << "derive::g1()" << endl; }
    virtual void h1() { cout << "derive::h1()" << endl; }
};

int main()
{
    typedef void (*fun)();

    base *b = new derive;
    cout << *(int*)b << endl;
    fun funf = (fun)(*(int*)*(int*)b);
    fun fung = (fun)(*((int*)*(int*)b 1));
    fun funh = (fun)(*((int*)*(int*)b 2));
    fun fung1 = (fun)(*((int*)*(int*)b 3));
    fun funh1 = (fun)(*((int*)*(int*)b 4));


    funf(); // derive::f()
    fung(); // base::g()
    funh(); // base::h()
    fung1(); // derive::g1()
    funh1(); // derive::h1()

    cout << (fun)(*((int*)*(int*)b 5));
    return 0;
}

   

从上表可以看出:

 

1.  覆盖的f()函数被放到了虚表中原来父类虚函数的位置。

2.  没有被覆盖的函数依旧。

3.  可通过获取获取成员函数指针来调用成员函数(即使是private类型的),带 来一定安全性的影响。

4. 多继承的情形 

#include
using namespace std;

class base1 {
public:
    virtual void f() { cout << "base1::f()" << endl; }
    virtual void g() { cout << "base1::g()" << endl; }
    virtual void h() { cout << "base1::h()" << endl; }
};

class base2 {
public:
    virtual void f() { cout << "base2::f()" << endl; }
    virtual void g() { cout << "base2::g()" << endl; }
    virtual void h() { cout << "base2::h()" << endl; }
};

class base3 {
public:
    virtual void f() { cout << "base3::f()" << endl; }
    virtual void g() { cout << "base3::g()" << endl; }
    virtual void h() { cout << "base3::h()" << endl; }
};

class derive: public base1,public base2, public base3 {
    virtual void f() { cout << "derive::f()" << endl; }
    virtual void g1() { cout << "derive::g1()" << endl; }
};

int main()
{
    typedef void (*fun)();

    derive d;
    base1 *b1 = &d;
    base2 *b2 = &d;
    base3 *b3 = &d;


    b1->f(); //derive::f()
    b2->f(); //derive::f()
    b3->f(); //derive::f()
    b1->g(); //base1::g()
    b2->g(); //base2::g()
    b3->g(); //base3::g()

    fun b1fun = (fun)(*(int*)*(int*)b1);
    fun b2fun = (fun)(*(int*)*((int*)b1 1));
    fun b3fun = (fun)(*(int*)*((int*)b1 2));

    b1fun(); // derive::f()
    b2fun(); // derive::f()
    b3fun(); // derive::f()

    return 0;
}

   

从上表可以看出:

1. 每个父类都有自己的虚表。

2. 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)

3. 对于多继承无虚函数覆盖的情况,布局与上图类似(derive的位置对应base)。

网站地图