Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4470200
  • 博文数量: 1148
  • 博客积分: 25453
  • 博客等级: 上将
  • 技术积分: 11949
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-06 21:14
文章分类

全部博文(1148)

文章存档

2012年(15)

2011年(1078)

2010年(58)

分类: C/C++

2011-05-30 21:43:16

多态性的概念:

多态性是面向对象程序设计的重要特征之一。
多态性是指发出同样的消息被不同类型的多态接收时导致完全不同的行为。

消息-- 主要是对类的成员函数的调用。

多态的实现:
1.函数重载
2.运算符重载        这两种都是静态: 编译时的多态
3.虚函数                     动态: 运行时的多态


//在编译的时候,就能决定 到底调用哪个函数,静态
函数重载例子:

int max(int a, int b);
float max(int a, int b);

void main()
{
      int x =
      int y =
      max(x, y);   //这就属于 函数重载, 静态
                  

}



联编 binding
     是指计算机程序自身彼此关联的过程联编工作在编译连接借款完成的情况称为: 静态联编
     联编在程序运行阶段完成的情况称为: 动态联编


运算符重载

函数类型 operator 运算符 (形参)
{
  ...
}



静态联编与动态联编

联编:
    程序自身彼此关联的过程,确定程序中的操作调用与执行该操作的代码间的关系。

静态联编:
    联编工作出现在编译阶段,用对象名或者类名来限定要调用的函数
动态联编:
    联编工作在程序运行时执行,在程序运行时才确定将要调用的函数



静态联编例子:
  1. #include <iostream>

  2. using namespace std;

  3. class Point
  4. {
  5. private:
  6.     double x, y;
  7. public:
  8.     Point(double i, double j)
  9.     {
  10.         x = i;
  11.         y = j;
  12.     }
  13.     double Area()const
  14.     {
  15.         return 0.0;
  16.     }
  17.     
  18. };

  19. class Rectangle:public Point
  20. {
  21. private:
  22.     double w, h;
  23. public:
  24.     Rectangle(double i, double j, double m, double n);
  25.     double Area()const
  26.     {
  27.         return w*h;
  28.     }
  29. };

  30. Rectangle::Rectangle(double i, double j, double m, double n):Point(i,j)
  31. {
  32.     w = m;
  33.     h = n;
  34. }

  35. void fun(Point &s)
  36. {
  37.     cout << "area=" << s.Area() << endl;
  38. }

  39. int main()
  40. {
  41.     Rectangle rec(3.0, 5.2, 15.0, 25.0);
  42.     fun(rec);
  43.     return 0;
  44. }
  1. ywx@yuweixian:~/yu/c++/chengxuyuan$ ./jingtailianbian
  2. area=0
    分析:程序本应该输出 15*25 的值,但是这里输出了 0 ,为什么呢??
void fun(Point &s);这个函数是 静态联编,在编译的时候就定义了 为Point 基类类型,所以只会调用基类的 Area 函数,返回 0.0


     可以把派生类 赋值给 基类


动态联编例子:使用virtual 虚函数

  1. virtual double Area()const
  2.         {
  3.                 return 0.0;
  4.         }


  5.   virtual double Area()const
  6.         {
  7.                 return w*h;
  8.         }
  1. ywx@yuweixian:~/yu/c++/chengxuyuan$ ./jingtailianbian
  2. area=375

虚函数:

1.虚函数是动态联编的基础。
2.是非静态的成员函数
3.在类的声明中,在函数原型之前写virtual
4.virtual 只用来说明类声明中的原型,不能用在函数实现时
5.具有继承性,基类中声明了虚函数,派生类中无论是否说明,同原型函数都自动为虚函数
6.本质:不是重载声明而是覆盖
7.调用方式: 通过基类指针或引用,执行时会根据指针指向的对象的类,决定调用哪个函数




抽象类:
抽象类的一般形式:

带有纯虚函数的类称为抽象类:
class 类名
{
   virtual 类型 函数名(参数表)=0;
                        //纯虚函数
}

作用:
1.  抽象类为抽象和设计的目的而建立,将有关的数据和行为组织在一个集成层次结构中,保证派生类具有要求的行为
2. 对于暂时无法实现的函数,可以声明为纯函数,留给派生类去实现。


注意: 抽象类只能作为 基类来使用
       不能声明抽象类的对象


  1. #include <iostream>

  2. using namespace std;

  3. class B0 //抽象基类 声明
  4. {
  5. public:
  6.     virtual void display()=0;
  7.         // 纯虚函数成员
  8. };

  9. class B1:public B0 //公有派生
  10. {
  11. public:
  12.     virtual void display()
  13.     {
  14.         cout << "B1::display()" << endl;
  15.     }
  16. };

  17. class D1:public B1 //公有派生
  18. {
  19. public:
  20.     virtual void display()
  21.     {
  22.         cout << "D1::displsy()" << endl;
  23.     }
  24. };

  25. void fun(B0 *ptr)
  26. {
  27.     ptr->display();
  28. }


  29. int main()
  30. {
  31.     B0 *p;
  32.     B1 b1;
  33.     D1 d1;
  34.     
  35.     p = &b1;
  36.     fun(p);

  37.     p = &d1;
  38.     fun(p);

  39.     return 0;
  40. }
  1. ywx@yuweixian:~/yu/c++/chengxuyuan$ ./chouxiang
  2. B1::display()
  3. D1::displsy()
   一个函数一旦被说明为虚函数,则无论说明他的类被继承了多少层,在每一层派生类中该函数都保持virtual特性。因此,在派生类中重新定义该函数时,不再需要关键字 virtual
   但习惯上,为了提高程序的可读性,常常在每层派生类中都重复的使用 virtual 关键字


虚函数的限制:

1. 只有类的成员函数才能说明为虚函数,因为虚函数仅适用于继承关系的类对象,所以普通函数不能说明为虚函数
2. 内联函数不能是虚函数,因为内联函数是在编译时决定其位置
3. 构造函数不能是虚函数,因为构造时对象还是一片为定型的空间。
4. 析构函数可以是虚函数,而且通常声明为虚函数。



下面的程序,一步一步说明 为什么析构函数 要为 虚函数 !!!!

1.构造与析构函数,运行时
  1. #include <iostream>

  2. using namespace std;

  3. class Base
  4. {
  5. public:    
  6.     Base()
  7.     {
  8.         cout << "construct in Base\n";
  9.     }
  10.     ~Base()
  11.     {
  12.         cout << "destrcting Base" << endl;
  13.     }
  14. };

  15. class Subclass:public Base
  16. {
  17. public:
  18.     Subclass()
  19.     {
  20.         cout << "construc in subcalss\n";
  21.     }
  22.     ~Subclass()
  23.     {
  24.         cout << "destructing subclass" << endl;
  25.     }
  26. };

  27. int main()
  28. {
  29.     cout << "first:\n";
  30.     Base bc;  //初始化, 需要构造函数
  31.     cout << "second\n";
  32.     Subclass sc;
  33.     cout << "end!\n";
  34.     return 0;
  35. }
  1. ywx@yuweixian:~/yu/c++/chengxuyuan$ ./xu
  2. first:
  3. construct in Base
  4. second
  5. construct in Base
  6. construc in subcalss

  7. destructing subclass   析构,反方向
  8. destrcting Base
  9. destrcting Base

2.指针
修改,程序为
  1. void test(Base *x) //派生类中,赋值兼容原则
  2. {
  3.     delete x;
  4. }
  5. int main()
  6. {
  7.     cout << "first:\n";
  8.     Base *bc = new Base;   //使用 new  初始化
  9.     cout << "second\n";
  10.     Subclass *sc = new Subclass;
  11.     cout << "calling test(bc)\n";
  12.     test(bc);
  13.     cout << "calling test(sc)\n";
  14.     test(sc);
  15.     cout << "end!\n";
  16.     return 0;
  17. }
  1. ywx@yuweixian:~/yu/c++/chengxuyuan$ ./xu
  2. first:
  3. construct in Base
  4. second
  5. construct in Base
  6. construc in subcalss
  7. calling test(bc)
  8. destrcting Base
  9. calling test(sc)
  10. destrcting Base

这里我们看到,在析构 subclass 时,
  1.  ~Subclass()
  2.     {
  3.         cout << "destructing subclass" << endl;
  4.     }
没有 调用自身的析构函数,只使用了基类的析构,因为在
  1. void test(Base *x) //派生类中,赋值兼容原则
  2. {
  3.     delete x;
  4. }
没有使用虚函数,所以test 函数 不是动态绑定,不能根据运行时的对象来选择 ,所以还是只使用了 编译时,基类的析构函数


所以要修改为  虚函数,增强不同  派生类的



virtual ~Base()
 13         {
 14                 cout << "destrcting Base" << endl;
 15         }



virtual
~Subclass()
 26         {
 27                 cout << "destructing subclass" << endl;
 28         }


  1. ywx@yuweixian:~/yu/c++/chengxuyuan$ ./xu
  2. first:
  3. construct in Base
  4. second
  5. construct in Base
  6. construc in subcalss
  7. calling test(bc)
  8. destrcting Base
  9. calling test(sc)
  10. destrcting Base
  11. destructing subclass



虚函数 析构函数是 特殊 的虚函数名字不一样

所以:
   1. 当在基类中把成员函数定义为虚函数后,在派生类中定义的虚函数必须与基类中的虚函数同参数的类型、顺序、个数必须一一对应。

    2. 实现这种动态的多态性,必须使用基类类型的 指针变量或引用,使该指针指向不同的派生类的对象,并通过调用指针所指的虚函数才能实现动态的多态性。



小结:

   多态: 同样的消息被不同类型的对象接收时导致完全不同的行为,是对类的特定成员函数的再抽象。
   运算符重载: 对已有的运算符赋予多重含义,使用已有运算符对用户自定义类型 进行运算操作。

   纯虚函数: 在基类中说明的虚函数,他在基类中可以不给出函数体,要求各派生根据实际需要编写自己的函数体。
   抽象类: 带有纯虚函数的类是抽象类。
            抽象类的主要作用是通过它为一个类族建立一个公共的接口,使他们能够更有效的发挥多台特征。



阅读(1536) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~