Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3034568
  • 博文数量: 167
  • 博客积分: 613
  • 博客等级: 中士
  • 技术积分: 5473
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-13 21:35
个人简介

人, 既无虎狼之爪牙,亦无狮象之力量,却能擒狼缚虎,驯狮猎象,无他,唯智慧耳。

文章分类
文章存档

2015年(19)

2014年(70)

2013年(54)

2012年(14)

2011年(10)

分类: C/C++

2013-08-12 16:54:21

     面向对象编程(OPP:Object-oriented programming)基于三个基本的概念:数据抽象、继承和动态绑定。之前在学习“基于对象的编程”时已经了解到了数据封装和抽象的作用,今天学习的这部分主要来讨论后两个重要的概念。如果说之前的类只是涉及一维层次的话,这里将涉及多个层次的类与类之间的关系。这部分的内容比较多,知识点繁杂,短时间不可能看透,所以自己就感觉相对重要的知识点简单进行一个梳理,作为日后继续研习提高的基石。
一、继承与动态绑定
     谈到继承,熟悉C++的人一定不陌生。可以选定一个类作为基类给派生类继承,派生类将继承基类的所有非static成员。派生类中的成员天然地可以看作两个部分:继承自基类的部分以及自身特性覆盖定义或新增的部分。我觉得这里最重要的是形成这样一种认识,派生类逻辑上是由“基类部分”+“自身部分”组合而成的,虽然物理上未必如此,因为C++并不规定派生类的基类成员与新增成员必须统一存储。基类可以提供接口供派生类继承,也可以指定为virtual函数要求派生类重新定义,如:
class  base
{
   public:
              virtual print()
                     {cout << "This is a base"< };
class  derived: public base
{
   public:
               print()
                     {cout << "This is a derived"<}
};
... ... 
class derived D-obj;
class base &ref = D-obj;
D-obj.print();
... ...
     例子虽然简单,但是可以看到一些最基本的知识点。首先在基类中可以定义一个虚函数,而后在派生类中对该虚函数进行“重定义”,这里会体现出派生类自己独特的属性;在用户代码中,定义了指向基类的指针绑定到派生类对象上,调用出派生类的print()行为,实现了动态绑定。其实动态绑定需要两个条件:一个条件时该操作被声明为虚函数;第二个条件则是利用指向基类的指针或者引用。由于派生类本身即包含着基类部分,因此可以使用基类的指针或者引用;另一个方面,也使得由派生类向基类的类型的自动转换成为可能,有些时候由派生类向基类转换时会舍弃派生类部分,比如:
void  fun-1(base B-obj)
{
B-obj.print();
}
fun-1(D-obj);                             //实参传递时D-obj去掉派生类部分的副本
     由于这里涉及到了虚函数(virtual),因此简单小结下虚函数需要注意的几个问题:
1. 派生类一般会重定义虚函数,否则使用基类中定义的版本;
2. 派生类中虚函数的声明必须与基类中的定义方式完全匹配,但是当虚函数返回基类型的引用或指针时除外;
3. 一旦声明为虚函数,则一直为虚函数,派生类重定义虚函数时,可以使用virtural关键字,也可以不用;

二、公有、私有和受保护的继承
     在派生类继承基类时,涉及到public\private\protected三种层级。首先需要明确的是派生类的继承层级只能严格基类本身的访问层级,不能放宽。即对于基类而言,其public成员可以供所有用户访问,private成员则只允许自身的函数访问。对于派生类而言,同样不能够访问基类的private成员。当发生类继承关系时,遵循着“最严格”原则,即总是取基类约束与继承约束最严格的访问等级。这种规则下,public继承不改变原有的基类的访问层级,而基类的protected成员可以由派生类访问但是却不能由普通的用户代码访问。之所以引入了protected,就是为了提供一种派生类可以访问而其他代码不能访问的控制层级。
     需要说明的是,基类声明为友元的对象当然可以访问基类的所有成员,包括private;但是友元关系不能继承,仅仅针对声明的对象成立。即:友元类的派生类继承对目标类的友元关系;目标类的派生类也不能继承被友元类访问的属性。说起来比较拗口,如下面简单的例子:

点击(此处)折叠或打开

  1. class Base {
  2.       friend class Frnd;
  3.       protected:
  4.                 int i;
  5.                 };
  6. class D1: public Base {
  7.       protected:
  8.                 int j;
  9.                 };
  10. class Frnd {
  11.       public:
  12.              int mem(Base b) {return b.i};
  13.              int mem(D1 d) {return d.i};                //Error:friendship doesn't inherit
  14.              };

  15. class D2: public Frnd {
  16.       public:
  17.              int mem(Base b) {return b.i}               //Error:friendship doesn't inherit
  18.              };
      Frnd作为友元类可以方位Base的成员,但那是却不能范围Base的派生类成员;Frnd的派生类也不能继承父类的友元属性,同样不能访问Base类。

三、构造函数与复制函数
     构造函数与复制控制成员不能继承,每个类定义自己的构造函数和复制控制成员。如同之前所说的情况,如果类不定义自己的默认构造函数和复制控制成员,就使用默认版本。自己觉得这里最重要的是认识到:派生类构造函数的构造顺序是先基类再派生类;析构函数是先派生类再基类;每个派生类的构造函数只能初始化其直接的父类,不能涉及其祖父辈的类。
阅读(3477) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~