C++学习笔记

C++继承与派生,虚函数与多态

2015-05-18  本文已影响270人  tengmoon

假设定义了以下的基类和派生类:

class shape
{
    public:
    shape(int a){};             //基类构造
    int length(){};             //基类中与子类重名函数
    void display(){};           //基类中独有
    vitual int area(){};        //基类中虚函数
}

class rectangle:public shape
{
    public:
    rectangle(int);
    int length();
    int area();

}

1. 派生类的定义

rectangle::rectangle(int a):shape(int a){};     //派生类中使用基类构造函数
void rectangle::length()
{
    shape::length();                            //派生类中使用重名的父类函数
    display();                                  //派生类中使用父类中独有函数
}

2. 派生类实例的使用

rectangle rect();
shape theshape();
rect.length();          //调用rectangle::length()
rect.shape::length();   //显式指定调用基类函数shape::length()
rect.display();         //display()被继承,调用shape::display()

3. 对象的成员变量与成员函数的不同

成员变量在对象初始化之后,仍然可以进行赋值,更改。事实上,对对象的任何操作,也仅仅是对它的成员变量进行操作。
成员函数在对象初始化那一刻使确定下来,成员函数是根据该对象的类型进行绑定的,是编译期已经确定的,静态绑定。

各个实例的成员变量的地址是不同的,与实例的首地址有关。
各个实例的成员函数其实是共用的,因为只与他们的类型相关。

成员变量的地址(包括虚表变量)是按照各实例的首地址给的,各实例之间不相同。
成员函数的地址是按照实例的类型给的,各实例间相同。

4. 类型的转换

要区分对象的类型转换对象指针的类型转换:

  1. 实际上,指针的类型可以任意转换,只是指针指向的地址发生变化而已。
  2. 对象的类型转换,实际上是将右值的所有成员变量的值赋给左值。注意:右值的虚表变量不会赋值给左值!
  3. 就地转换,实际上是隐含地调用了一次转换后类型的构造函数,产生了一个临时对象,将这个临时对象作为左值。

    rectangle rect;
    shape shp;
    shp = (shape)rect;          //合法,将rect中与shp相同的那部分成员变量赋给shp
                                //等价于 shp.a = rect.a, shp.b = rect.b
                                // shp.vtable并没有被rect.vtable覆盖! 仍然是原来的。

    rect = (rectangle)shp;      //不合法,因为rect的一些成员变量,shp并没有,所以赋值时要出错
                                //rect.a  = shp.a, rect.b = shp.b,rect.c = shp.?
    rectangle* prect;
    shape* pshp;
    pshp = ▭               //pshp指向了rect的首地址
                                //pshp指向的成员变量是rect的成员变量
                                //(pshp可以指向一些shape类型没有的成员变量)
                                // 但pshp绑定的成员函数还是shape类型的成员函数。
                                //pshp->vtable就是rect.vtable, 但pshp->length()调用的是 shape::length();
    prect = &shp;        //合法,但是没有实际意义

5. 虚成员函数与普通成员函数

上面已提了,一个实例的普通成员函数在编译期已经根据它的类型确实了。而虚函数不是这样的,它的调用是根椐实例的成员变量虚表查到的。

rectangle rect;
shape shp;
shp.area();                 //其实是shp.vtable->area();
rect.area();                //rect.vtable->area();

shape* pshp;
pshp = ▭
pshp->area();               //实际上是pshp->vtable->area(),而pshp->vtable == rect.vtable,所以调用的是 rectangle::area();
pshp->length();             //length是普通的成员函数,根据pshp的类型确定,所以调用的是shape::length();


((shape)rect).area();       //就地转换并调用,相当于下面的的两行代码
shape shp2 = rect;
shp2.area();                //shp2的成员变量被rect的成员变量覆盖,但shp2.vtable并没有变成rect.vtable,仍然调用shape::area();

6. 虚函数的用途:统一形式的调用

shape* pshps = [&triangle(), &rectangel()];
for(auto ipshp: pshps)
    ipshp->area();          //分别调用了各个不同类型实例的area();
上一篇下一篇

猜你喜欢

热点阅读