封装/继承/多态

封装/继承/多态

面向对象的编程语言不止仅仅多了个类,类可以明显感觉到抽象和封装。抽象的是具体的实现,封装的是接口。

继承,分为实现继承,接口继承,可视化继承。

实现继承就是重新写一个函数去实现。这儿用的是纯虚函数。父辈纯虚,子辈必须重新实现。

接口继承是指纯粹继承父辈的老底,也可以去重新实现。这是虚函数。

可视化继承则是纯粹的全部自己实现,自己功能自己解决。对应的是非虚函数。

继承的时候父子有几种关系,比如B继承A,一种是泛化,换句话就是b是a的一种,比如老师一种人,学生是一种人。杀人犯也是一种人。这叫泛化。这儿可以接口继承或者可视化继承。因为本质是一种的关系么。这儿可以直接用父辈的老底。

在一中是聚合,聚合呢就是b是a的一部分,但是b不是一种a,比如头有眼睛鼻子嘴巴耳朵,这样就要独自去实现。这儿用到的是接口继承和纯虚类。毕竟不能说眼睛是头的一种么,要自己去实现很多的东西。

以上继承的本质是为了更方便的代码复用,而多态则是接口复用。接口复用可以用重载函数来做,也可以用类的多态性来进行。

多态,则更多的是可以用子辈赋给父辈的引用或者指针,然后可以在运行的时候去实现访问子类。这样就看似是一个,还有待补充。慢慢来。看图很不错的。

多态代码

[cpp] view plain copy
  1. //小结:1、有virtual才可能发生多态现象    
  2. // 2、不发生多态(无virtual)调用就按原类型调用    
  3. #include<iostream>    
  4. using namespace std;    
  5.     
  6. class Base    
  7. {    
  8. public:    
  9.     virtual void f(float x)    
  10.     {    
  11.         cout<<"Base::f(float)"<< x <<endl;    
  12.     }    
  13.     void g(float x)    
  14.     {    
  15.         cout<<"Base::g(float)"<< x <<endl;    
  16.     }    
  17.     void h(float x)    
  18.     {    
  19.         cout<<"Base::h(float)"<< x <<endl;    
  20.     }    
  21. };    
  22. class Derived : public Base    
  23. {    
  24. public:    
  25.     virtual void f(float x)    
  26.     {    
  27.         cout<<"Derived::f(float)"<< x <<endl;   //多态、覆盖    
  28.     }    
  29.     void g(int x)    
  30.     {    
  31.         cout<<"Derived::g(int)"<< x <<endl;     //隐藏    
  32.     }    
  33.     void h(float x)    
  34.     {    
  35.         cout<<"Derived::h(float)"<< x <<endl;   //隐藏    
  36.     }    
  37. };    
  38. int main(void)    
  39. {    
  40.     Derived d;    
  41.     Base *pb = &d;    
  42.     Derived *pd = &d;    
  43.     // Good : behavior depends solely on type of the object    
  44.     pb->f(3.14f);   // Derived::f(float) 3.14    
  45.     pd->f(3.14f);   // Derived::f(float) 3.14    
  46.     
  47.     // Bad : behavior depends on type of the pointer    
  48.     pb->g(3.14f);   // Base::g(float)  3.14    
  49.     pd->g(3.14f);   // Derived::g(int) 3     
  50.     
  51.     // Bad : behavior depends on type of the pointer    
  52.     pb->h(3.14f);   // Base::h(float) 3.14    
  53.     pd->h(3.14f);   // Derived::h(float) 3.14    
  54.     return 0;    
  55. }  


令人迷惑的隐藏规则
本来仅仅区别重载与覆盖并不算困难,但是C++的隐藏规则使问题复杂性陡然增加。
这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual
关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual
关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
上面的程序中:
(1)函数Derived::f(float)覆盖了Base::f(float)。
(2)函数Derived::g(int)隐藏了Base::g(float),而不是重载。

(3)函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。


1.封装是面向对象最重要的特征之一(C++中的类class),封装就是指隐藏。 

封装的优点:(1)确保用户代码不会无意间破坏封装对象的状态;(外部程序不能随便改变对象中的状态)(2)被封装的类的具体实现细节可以随时改变,而无需调整用户级别的代码(底层的实现与上层的接口无关)

2.继承是面向对象的又一重要特征。继承可以使用不同的类的对象具有相同的行为;为了使用其他类的方法,我们没有必要重新编写这些旧方法,只要这个类(子类)继承包含的方法的类(父类)即可。 从下往上看,继承可以重用父类的功能;从上往下看,继承可以扩展父类的功能。


3、多态多态可以使我们以相同的方式处理不同类型的对象。我们可以使用一段代码处理不同类型的对象,只要他们继承/实现了相同的类型。这样,我们没有必要为每一种类型的对象撰写相同的逻辑,极大的提高了代码的重用。 


C语言是一种面向过程的程序设计语言,而C++是在C语言基础上衍生来了的面向对象的语言,实际上,很多C++实现的底层是用C语言实现的(http://www.ibm.com/developerworks/cn/linux/l-cn-cpolym/index.html?ca=drs-)

1.C语言中的封装

在C语言中,可以用结构+函数指针来模拟类的实现,而用这种结构定义的变量就是对象。

封装的主要含义是隐藏内部的行为和信息,使用者只用看到对外提供的接口和公开的信息。有两种方法实现封装:

(1)    利用C语言语法。在头文件中声明,在C文件中真正定义它。(只将非static的函数和变量暴露出来)

(2)    把私有数据信息放在一个不透明的priv变量或者结构体中。只有类的实现代码才知道priv或者结构体的真正定义。(外部无法直接修改struct中的变量)


2.C语言中的继承

在C语言中,可以利用“结构在内存中的布局与结构的声明具有一致的顺序”这一事实实现继承。

[cpp] view plain copy
  1. struct Circle  
  2. {  
  3. const struct Point point; //放在第一位,可表继承  
  4. int radius;  
  5. };  

3.C语言中的多态

可以使用C语言中的万能指针void* 实现多态


一,static和extern:

大工程下我们会碰到很多源文档。
                
文档a.c
static int i; //只在a文档中用
int j;    //在工程里用
static void init()         //只在a文档中用
{
}
void callme()          //在工程中用
{
   static int sum;
}

上面的全局i变量和init()函数只能用在a.c文档中,全局变量sum的作用域只在callme里。变量j和函数callme()的全局限扩充到整个工程文档。所以能够在下面的b.c中用extern关键字调用。extern告诉编译器这个变量或函数在其他文档里已被定义了。
文档b.c
extern int j;     //调用a文档里的
extern void callme();  //调用a文档里的
int main()
{
  ...
}

extern的另外用法是当C和C++混合编程时假如c++调用的是c源文档定义的函数或变量,那么要加extern来告诉编译器用c方式命名函数:
文档A.cpp调用a.c里面的变量i和函数callme()
extern "C"  //在c++文档里调用c文档中的变量
{
   int j;
   void callme();
}
int main()
{
   callme();
}

二,static法则:

    A、若全局变量仅在单个C文档中访问,则能够将这个变量修改为静态全局变量,以降低模块间的耦合度;
    B、若全局变量仅由单个函数访问,则能够将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
    C、设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题;