2010年10月13日星期三

论虚函数和多态

虚函数和多态
2007-06-23 20:33

二.虚函数和多态

1. 先看以下的例子:

class Animal

{

private:

double weight;

string color;

public:

Animal(double a, string b) : weight(a), color(b) {}

virtual void roar() {cout << "roar in the base class!" <<>

void printName()

{cout << "my weight is: " <<>

};

class Cattle : public Animal

{

public:

Cattle(double a, string b) : Animal(a, b){}

//void roar(){ cout << "Cattle is roaring!" <<>

};

class Dog : public Animal

{

public:

Dog(double a, string b) : Animal(a,b) {}

//void roar(){ cout << "Dog is barking!" <<>

};

……

Animal *myAnimal;

Cattle myCattle(100, "brown");

Dog myDog(10, "black");

myAnimal = &myCattle; // 通过基类指针正确调用子类函数,这便是多态

myAnimal->roar();

//myAnimal->printName();

myAnimal = &myDog;

myAnimal->roar();

//myAnimal->printName();

//myCattle.printName();

//myDog.printName();

Polymorphism: the ability to assume many forms

注意点:

1. 派生类继承基类的所有,包括成员函数和成员变量,即使虚函数也将被继承。此处,对于public继承,派生类可以直接调用基类中的public成员函数,此public成员函数可以直接访问基类中的private变量;但是,派生类中不能不通过基类继承来的成员函数直接去访问基类的成员变量,如此处的weightcolor。它们一定要通过Animal类中的priteName()函数来访问。

2. 包含纯虚函数(virtual func( )=0)的类是抽象类,不能被实例化,否则编译出错。

3. 包含虚函数类的对象的内存空间:

举例:

Class Class1

{

public:

data1;

data2;

memfunc();

virtual vfunc1();

virtual vfunc2();

virtual vfunc3();

}

在内存空间中,表示如下:

从上表中可以看出每个含有虚函数的类,编译器为其分配一个虚函数表(vtable),表中每一项元素都指向一个虚函数的地址。同时,编译器为类加上一个成员变量,就是指向虚函数表的指针(vptr)

值得注意的是,C++类的普通成员函数并没有出现在Class1的内存区块中。可以将其想象成是C语言中的函数,它只是被编译器改过名字,并增加了一个this指针而已。因此不需要出现在类实例的内存中。

class1继承而来的类因为会继承它的虚函数,因此也有自己的vptr。派生类继承基类的虚函数表以及其它可以继承的东西。但是,如果我们在派生类中改写了虚函数时,虚函数表将改变:表中元素所指向的将不再是基类的函数地址,而是被修改的虚函数新地址。【此处一个重要信息就是:在未修改基类虚函数时,在派生类中调用,将会调用基类中的函数】

看下例:

class class2 : public class1

{

public:

data3;

memfunc();

virtual vfunc2(); // 修改了基类中的一个虚函数

}

4. upcasting

Class Father

{
public:
void func()
{
cout<<"this is the Father func!"< vfunc();
}
virtual void vfunc()
{
cout<<"this is the Father vfunc!"< }
private:
int m_data1;
}


Class Son

{
void vfunc()
{
cout<<"this is the Son vfunc!"< }
private:
int m_data2;
};

Farher father; Son son;
(1) ((father *)(&son))->vfunc();
(2) ((father)son).vfunc();
:在(2)中对象发生的截断,son已经完完全全的变成了father型对象,所以(2)调用的虚函数是father中的,但是我总觉得就算发生了截断,son对象中的vptr应该是不变的吧??,,为什么会变?为什么将子类指针强制转化为父类指针(1),不发生截断呢?

:所谓的切片(slice)就是指创建出了一个临时的对象,你没看到(father)son这是一个强制转换嘛。son中的vptr当然没变,(father)son已经是一个father类型的临时对象了,其vptr当然指向father类的了

没有评论: