C/C++学习----第三章 多态性和虚函数

第三章 多态性和虚函数

3.1 多态性

3.1.1 编译时的多态性——重载

    重载的特征是:各个函数的名字相同,但参数个数或参数的类型不同。

    在派生类中调用基类的重载函数时,必须带有基类名和域作用符(::),否则引起派生类函数的无限循环调用。

    <1>在一个类中进行重载

    <2>基类成员函数在派生类中重载

<3>类外部函数的重载

C++中,大多数系统预定义的运算符都可以重载。除以下运算符:.  ::  .*  ?: sizeof

class point {

    int x, y;

public:

    point(int x, int y);

    point operator +( point p); //重载运算符+

    point operator -( point p); //重载运算符-

    point operator =( point p); //重载运算符=

}

重载不能建立新的运算符,也不能改改变运算符的优先级、结合性和操作数的个数。赋值运算符(=)和地址运算符(&)无需重载就可以用于每一个类。

 

3.1.2 运行时的多态性——虚函数

    多态性是通过虚函数实现的,体现在:通过基类指针(或引用)请求使用虚函数时,C++会在与对象关联的派生类中正确的选择重定义的函数。派生类指针调用成员函数时,不是多态性行为;以对象与“.”符调用虚函数,也不是多态性行为。

对象指针:

<1>可以让一个指向基类的指针指向公有派生的对象,但是不能指向一个私有派生的对象。

<2>不能将一个指向派生类的指针指向基类对象

<3>声明为基类对象的指针,当指向派生类对象时,只能利用它来访问派生类中从基类继承来的成员,不能直接访问公有派生类中特定的成员。如果要访问公有派生类中的特定成员,需要将基类指针显式类型转换为派生类指针来实现(如下表左部)。

 

class A {

public:   

    print();

};

class B: public A{

public:

    print();

};

 

void main()

{

    B b, *pb;

    A a, *pa;

    pa = &b;

    pa->print(); //访问类A中的print

    ((B *)pa)->print(); //访问类B中的print

}

 

class A {

public:   

    virtual print();

};

class B: public A{

public:

    print();

};

 

void main()

{

    B b, *pb;

    A a, *pa;

    pa = &b;

    pa->print(); //访问类B中的print

}

 

虚函数:

    在基类中,声明了一个虚函数,在它的派生类中可被重新定义,函数的原型必须完全一致(不同于基类和派生类中的函数重载),否则系统会认为是函数重载;如果仅仅时返回值不同,参数类型和格数完全相同,系统认为是一种语法错误。指向基类的指针,当它指向不同的派生类对象时,调用不同对象的成员函数(体现了动态多态性)。

实现虚函数需要对象附带一些额外信息,以使对象在运行时可以确定该调用哪个虚函数。对大多数编译器来说,这个额外信息的具体形式是一个称为vptr(虚函数表指针)的指针。vptr指向的是一个称为vtbl(虚函数表)的函数指针数组。每个有虚函数的类都附带有一个vtbl。当对一个对象的某个虚函数进行请求调用时,实际被调用的函数是根据指向vtbl的vptr在vtbl里找到相应的函数指针来确定的。详细请参见F:/useful/ATL Under the Hood.doc

    多继承中的虚函数:

    虚函数特性是可以传递的,多个基类的公共基类中的虚函数可以传递给多重派生类,基类指针不能指向派生类的派生类对象。

class A {

public:   

    virtual void print();

};

class B{

public:

    void print( );

};

class C: public A, public B{

public:

    void print( );

};

 

void main()

{

    A a, *pa;

    B b, *pb;

    C c;

    pa = &c;   pa->print();

    pb = &c; //调用C中的print, 体现虚函数特性

    pb->print();//调用基类B中的print,体现重载函数特性

}

纯虚函数:

    纯虚函数是一个在基类中声明的虚函数,但在基类中没有定义,要求所有派生类必须定义自己的版本。形式:virtual type funname(参数表)=0;如果在抽象类的派生类中,没有提供纯虚函数的定义,该函数仍为纯虚函数,该类为抽象类。

抽象类:

    如果一个类至少有一个纯虚函数,则称为抽象类。对抽象类

    <1>不能建立抽象类的对象

    <2>抽象类不能用作参数类型,函数返回值或显式转换的类型

    <3>可以声明抽象类的指针和应用,此指针可以指向派生类,实现多态性。

class awov {                // awov = "abstract w/o

    // virtuals"

public:

    virtual ~awov() = 0;      //声明一个纯虚析构函数

};

这个类有一个纯虚函数,所以它是抽象的,而且它有一个虚析构函数,所以不会产生析构函数问题。但这里还有一件事:必须提供纯虚析构函数的定义:

awov::~awov() {}          // 纯虚析构函数的定义

这个定义是必需的,因为虚析构函数工作的方式是:最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用。这就是说,即使是抽象类,编译器也要产生对~awov的调用,所以要保证为它提供函数体。 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pony12

助力1000篇OpenDDS文

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值