例题一
#include <iostream>
using namespace std;class Animal
{
public:Animal(string name) :name_(name) {}virtual void bark() = 0;
protected:string getName() { return name_; }
private:string name_;
};class Cat : public Animal {
public:Cat(string name) : Animal(name){}void bark() {cout << "Cat " << getName() << "miao miao miao" << endl;}
};
class Dog : public Animal {
public:Dog(string name) : Animal(name) {}void bark() {cout << "Dog " << getName() << "wang wang wang" << endl;}
};
int main()
{Animal* cat = new Cat("罗小黑");Animal* dog = new Dog("布鲁托");int* p = (int*)cat;int* q = (int*)dog;int tmp = p[0];p[0] = q[0];q[0] = tmp;cat->bark();dog->bark();
}
运行结果: Dog 罗小黑wang wang wang
Cat 布鲁托miao miao miao
原因:将Cat与Dog的虚表指针vfptr交换了,导致调用的函数互换了。
例题二
class Base {
public:virtual void show(int i = 10){cout << "Base::show() " << i << endl;}
};class Drived : public Base
{
public:virtual void show(int i = 20){cout << "Drived::show() " << i << endl;}
};
int main()
{Base* b = new Drived;b->show();
}
运行结果 Drived::show() 10
原因:编译阶段,会将基类的show()函数,如果该函数有默认形参那么将其形参默认值入栈。但是运行时期,是调用的虚函数表的函数地址。
具体汇编代码如下,
b->show();
00007FF66DED2692 mov rax,qword ptr [b]
00007FF66DED2696 mov rax,qword ptr [rax]
00007FF66DED2699 mov edx,0Ah // 基类的show()默认形参10
00007FF66DED269E mov rcx,qword ptr [b]
00007FF66DED26A2 call qword ptr [rax]
例题三
class Base {
public:virtual void show(){cout << "Base::show() " << endl;}
};class Drived : public Base
{
private:virtual void show(){cout << "Drived::show() " << endl;}
};
int main()
{Base* b = new Drived;b->show();delete b;
}
运行结果:Drived::show()
原因:编译阶段,编译器首先是否拥有函数访问权限。代码中,使用的是基类指针,所以编译器检查的是基类中show()的权限。而具体调用的show()是在运行阶段,因为基类使用虚函数,所以调用show()的时候动态绑定。尽管派生类的show()函数是私有的,但是其地址是在虚函数表中,因此从虚表中调用派生类的show()函数。
例题四
class Base {
public:Base(){cout << "Base Construct" << endl;clear();}void clear(){memset(this, 0, sizeof(*this));}virtual void show(){cout << "Base::show() " << endl;}
};class Drived : public Base
{public:Drived() {cout << "Drived Construct" << endl;}virtual void show(){cout << "Drived::show() " << endl;}
};
int main()
{/*Base* b1 = new Base(); // 运行错误b1->show();*/Base* b2 = new Drived; // 运行失败b2->show();
}