赞
踩
首先明确一个空类产生的对象的大小为1B,即使是一个空类,其实例化的对象至少占用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的普通成员函数,并不会占用类对象的内存空间。然而一旦我们向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>}
当类A中至少包含一个虚函数,编译器会为类A产生一个虚函数表vtbl,这个虚函数表会一直伴随着类A,包括其装入内存
虚函数表指针被赋值的时机:执行A的构造函数时,让对象的虚函数指针指向类A的vtbl
每个类对象的虚函数表指针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也一同被调用:
$ g++ virtual-dtor.cpp
$ ./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; }
$ g++ virtual-dtor.cpp
$ ./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(); }
存在继承关系时虚函数表指针指向:
考虑下面的继承关系和重写:
class Base { public: virtual void f() {} virtual void g() {} virtual void h() {} }; class Derive :public Base { public: virtual void g() {} //rewrite g() }; int main(int argc, char* argv[]) { Base b; Derive d; system("pause"); return 0; }
设父类有f(), g(), h()这三个虚函数,子类重写了父类中的g()虚函数,则父子类对象的虚函数表指针和虚函数表如下:

延申思考题:
对于下面的多态实例:
#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; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。