目录
例题一
#include<iostream>
#include<string>
using namespace std;
class Animal
{
public:
Animal(string name) :_name(name){}
virtual void bark() = 0;
protected:
string _name;
};
class Cat :public Animal
{
public:
Cat(string name) :Animal(name) {}
void bark()
{
cout << _name << "bark : miao miao!" << endl;
}
};
class Dog :public Animal
{
public:
Dog(string name):Animal(name){}
void bark()
{
cout << _name << "bark : wang wang!" << endl;
}
};
int main()
{
Animal* p1 = new Cat("加菲猫");
Animal* p2 = new Dog("二哈");
int* p11 = (int*)p1;
int* p22 = (int*)p2;
int tmp = p11[0];
p11[0] = p22[0];
p22[0] = tmp;
p1->bark();
p2->bark();
return 0;
}
运行结果:
例题分析:
两个基类指针p1和p2分别指向了两个派生类对象Cat和Dog,接着将两个基类指针p1和p2强转为int*类型。我们知道,抽象类构造出来的的对象的前4个字节为虚函数指针vfptr的内存空间。而在上述代码中,我们将p1和p2的vfptr进行了调换,因此当p1和p2分别调用虚函数bark()时,发生了动多态,系统会根据vfptr所指向的vftable去寻找虚函数bark()的地址,因为此时p1和p2中的vfptr已经互换,所以p1会调用Dog类中的虚函数bark(),而p2则会调用Cat类中的虚函数bark()。
例题二
#include<iostream>
using namespace std;
class Base
{
public:
virtual void show(int i = 10)
{
cout << "call Base::show i: " << i << endl;
}
};
class Derive : public Base
{
public:
void show(int i = 20)
{
cout << "call Derive::show i: " << i << endl;
}
};
int main()
{
Base* p = new Derive();
p->show();
delete p;
return 0;
}
执行结果:
例题分析:
首先我们让基类指针p指向了派生类对象,接着使用指针p调用了虚函数show(),发生了动多态,但我们发现打印出来的i的值却是基类中show()函数i的值,这是因为形参入栈是发生在编译时期的,而动多态是在运行时期发生的。当在编译时期,代码执行到
p->show();
这一步时,因为p是Base类型的指针,因此,此时发生的是静态的绑定,系统认为p调用的是Base类中的show()函数,因此此时参数i入栈,且此时i的值为10,而当运行阶段,代码再次执行到
P->show();
这一步时,因为show是虚函数,因此发生了动多态,调用Derive类中的show()函数,但此时打印的i的值是之前在编译间段就已经入栈的i,即最终打印的i的值为10。
例题三
#include<iostream>
using namespace std;
class Base
{
public:
virtual void show()
{
cout << "call Base::show()"<< endl;
}
};
class Derive : public Base
{
private:
void show()
{
cout << "call Derive::show()" << endl;
}
};
int main()
{
Base* p = new Derive();
p->show();
delete p;
return 0;
}
执行结果:
例题分析:
首先,我们用一个基类指针p指向了一个派生类对象,接着用这个指针p调用了虚函数show(),在编译间段,当代码执行到
p->show();
这一行时, 因为p是Base类型的指针,此时发生的是静态的绑定,系统认为p调用的是Base类中的show()函数,且我们注意到Base类中的show()函数的访问限定符是public,即可以在类外调用。
而当运行阶段,代码再次执行到
P->show();
这一步时,因为show是虚函数,因此发生了动多态,调用Derive类中的show()函数,我们知道调用虚函数的过程是p在vfptr所指向的vftable中寻找虚函数show()的地址,找到这个地址后,直接调用该函数,此时已与show()的函数访问限定符无关,所以,虽然派生类中的show()是私有的,但最终发生动多态时,我们依然可以调用它。
例题四
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "call Base()" << endl;
clear();
}
void clear()
{
memset(this, 0, sizeof(*this));
}
virtual void show()
{
cout << "call Base::show()"<< endl;
}
};
class Derive : public Base
{
public:
Derive()
{
cout << "call Derive()" << endl;
}
void show()
{
cout << "call Derive::show() " << endl;
}
};
int main()
{
Base* p1 = new Base();
p1->show();
delete p1;
//Base* p2 = new Derive();
//p2->show();
//delete p2;
return 0;
}
运行结果:
例题分析:
我们发现最终的运行结果显示程序执行错误。原因是,我们用一个基类指针指向了一个基类对象,在构造这个基类对象的时候会调用它的构造函数,而在它的构造函数中我们又调用了clear()函数,将这个基类对象所占内存空间的值全部赋为0,即此时vfpter的值也变为了0,所以虽然在程序运行间段发生了动多态,但因为vfptr的值为0,系统无法根据vfptr找到虚函数show()的入口地址,那么也就无法调用虚函数show()。
例题五
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "call Base()" << endl;
clear();
}
void clear()
{
memset(this, 0, sizeof(*this));
}
virtual void show()
{
cout << "call Base::show()"<< endl;
}
};
class Derive : public Base
{
public:
Derive()
{
cout << "call Derive()" << endl;
}
void show()
{
cout << "call Derive::show() " << endl;
}
};
int main()
{
// Base* p1 = new Base();
// p1->show();
// delete p1;
Base* p2 = new Derive();
p2->show();
delete p2;
return 0;
}
运行结果:
例题分析:
我们定义了一个基类指针指向了一个派生类对象,在构造这个派生类对象的时候会先构造基类部分,因此就会调用基类的构造函数,而基类的构造函数中又调用了clear()函数,将派生类对象中基类部分所占的内存空间的值赋为0,即此时vfptr的值为0,构造完基类部分后,就会构造派生类自己的那部分,此时就会将vfptr指向派生类自己的vftable,因此在程序运行间段,当p2调用虚函数show()的时候就会发生动多态,即p2最终会在vfptr所指向的vftable中寻找派生类中show()函数的入口地址,接着调用它。