Inheritance&&polymorphism

层次概念是计算机的重要概念。通过继承(inheritance)的机制可对类(class)分层,提供类型/子类型的关系。C++通过类派生(class
derivation)机制来支持继承。继承是使子类可以使用父类的成员,达到代码的复用和类的抽象

   被继承的类型称为基类(base class)或超类(superclass),新产生的类为派生类(derived
class)或子类(subclass)。基类和派生类的集合称作类继承层次结构(hierarchy)。如果基类和派生类共享相同的公有接口,则派生类被称作类的子类型(subtype)

C++类继承中总共可以通过三个方式来实现,包括:

1)、公有继承(public)

子类可以使用父类的所有成员(除private,访问private 成员必须通过共有成员函数来访问),包括(pubic,protected),继承后所有父类在子类中的权限类型不变,也就是说public还是public ,private还是private,protected还是protected

2)、私有继承(private),

子类可以使用父类的所有成员除(private,访问private 成员必须通过共有成员函数来访问),包括(pubic,protected),继承之后再子类中全部变为私有private

3)、保护继承(protected)

子类可以使用父类的所有成员除(private,访问private 成员必须通过共有成员函数来访问),包括(pubic,protected),继承的父类成员全部变为保护型protected(除private成员,private任然为private)。

总结如下表:

派生方式

基类中的访问权限类型

派生类中对继承的基类访问权限类型

子类对象对继承父类的成员的访问权限

public

Public

public

可直接访问

Protected

protected

不可直接访问(通过公有成员函数)

private

不可直接访问(通过公有成员函数)

不可直接访问(通过公有成员函数)

protected

Public

Protected

不可直接访问(通过公有成员函数)

Protected

Protected

不可直接访问(通过公有成员函数)

private

不可直接访问(通过公有成员函数)

不可直接访问(通过公有成员函数)

private

Public

Private

不可直接访问(通过公有成员函数)

Protected

Private

不可直接访问(通过公有成员函数)

private

不可直接访问(通过公有成员函数)

不可直接访问(通过公有成员函数)

继承可以使现有的代码具有可重用性和可扩展性。但是,在C++的编程规范中(如google的编程规范),不建议使用私有继承和保护继承,而是使用组合方式。

Example 1:

 

//Access levels

 

#include <iostream.h>

 

class Base

{

private:

   intpriv;

protected:

   intprot;

   intget_priv( ) {return priv;}

public:

   intpubl;

   Base();

   Base(inta, int b, int c) : priv(a), prot(b), publ(c) { }

   intget_prot( ) {return prot;}

   intget_publ( ) {return publ;}

};

 

class Derived1 : private Base   // private inheritance

{

public:

   Derived1(int a, int b, int c) : Base(a, b, c) { }

   intget1_priv( ) {return get_priv( );}

   //priv not accessible directly

   intget1_prot( ) {return prot;}

      int get1_publ( ) {return publ;}

};

 

class Leaf1 : public Derived1

{

public:

   Leaf1(inta, int b, int c) : Derived1(a, b, c) { }

   voidprint( )

   {

      cout<< "Leaf1 members: " << get1_priv( ) << ""

//       <<get_priv( )  // not accessible

         <<get1_prot( ) << " "

//       <<get_prot( ) // not accessible

//       <<publ // not accessible

         <<get1_publ( ) << endl;

   }  // data members not accessible.  get_ functions in Base not accessible

};

 

class Derived2 : protected Base //protected inheritance

{

public:

   Derived2(int a, int b, int c) : Base(a, b, c) { }

};

 

class Leaf2 : public Derived2

{

public:

   Leaf2(inta, int b, int c) : Derived2(a, b, c) { }

   voidprint( )

   {

      cout<< "Leaf2 members: " << get_priv( ) << ""

//       <<priv     // not accessible

         <<prot << " "

         <<publ << endl;

   }  // public and protected data membersaccessible.  get_ functions in Baseaccessible.

};

 

class Derived3 : public Base  // public inheritance

{

public:

   Derived3(int a, int b, int c) : Base(a, b, c) { }

};

 

class Leaf3 : public Derived3

{

 

public:

   Leaf3(inta, int b, int c) : Derived3(a, b, c) { }

   voidprint( )

   {

      cout<< "Leaf3 members: " << get_priv( ) << ""

      // << priv<< " "      // not accessible

         <<prot << " "

         <<publ << endl;

   }  // public and protected data membersaccessible.  get_ functions in Baseaccessible

};

 

int main( )

{

   Derived1d1(1, 2, 3);

   Derived2d2(4, 5, 6);

   Derived3d3(7, 8, 9);

 

// cout<< d1.publ;      // not accessible

// cout<< d1.get_priv( );  // notaccessible

// cout<< d2.publ;      // not accessible

// cout<< d2.get_priv( );  // notaccessible

   cout<< d3.publ;      // OK

   cout<< d3.get_prot( );  // OK

 

   Leaf1lf1(1, 2, 3);

   Leaf2lf2(4, 5, 6);

   Leaf3lf3(7, 8, 9);

 

//    cout << lf1.publ << endl;        //not accessible

//    cout << lf2.publ << endl;    // not accessible

   cout<< lf3.publ << endl;       //OK

 

   return0;

}

Using derived class objects as base classobject .把子类对象当做父类来使用。

当一个类继承了一个类之后,可以吧子类的对象赋值给父类对象,

Eg:

Derived d;

Base * p=new Base;

*p=d;

这仅仅是把子类对象中继承自父类的哪一部分赋值给等号左边的父类对象,而赋值玩以后等号左边的对象依然是父类对象,等号右边的依然是子类对象,这种情况适用于,指针,应用,传参数,函数返回值。当吧一个子类对象传递给形参是父类对象的函数时,会调用父类的拷贝构造,吧这个子类对象传递给父类拷贝构造参数中的父类应用,从而来创建一个子类对象,

父类指针指向子类对象,(把子类对象的地址复制给父类对象的指针),吧父类对象赋值给子类对象是非法的,因为子类对象中包括的父类的成员还有子类自己实现的成员,

当吧一个子类对象的地址赋值给父类对象的指针或者引用时,这个时候父类类型的指针调用成员函数时可能有两种表现形式,一种是父类中的函数在子类中重写(Override)了,但是没有被指定为虚(virtual)函数,这个时候指针任然调用的父类当中的成员函数.当父类中的成员函数被指定为虚函数,并且在子类中重写之后,这个时候就调用子类中的函数,这个时候也就是表现的是指针或者引用的本质特征了,就是他们只想谁调用谁里面的函数,这就是实现了多态。

Eg:

Example 5:

//Exampleof treating derived class object as base class objects. Point------Circle

#include<iostream.h>

 

//THE POINT CLASS

 

classPoint

{

friend ostream &operator<<(ostream &, Point &);

public:

Point(double xval =0, double yval=0 ) { x=xval; y=yval;};  

 

public:

voidprint()

{

cout<<" Point:X:Y: "<<x << ","<<y<< "\n";

}

 

protected:       // accessed by derived class

double x;   

double  y;

};

 

ostream &operator << (ostream & os, Point &  apoint)

{

os <<" Point:X:Y: "<<apoint.x <<","<< apoint.y<< "\n";

  returnos; 

}

 

 

//TheCircle class  inherits from class Point

classCircle : public Point

{

 

friend ostream &operator<<(ostream &,Circle&);

 

public:

Circle(double r=0,double xval=0,double yval=0):Point(xval,yval)

{radius = r;};

 

voidprint()

{

cout<<"Circle:radius:" <<radius<<endl;

cout<<" Point:X:Y: "<<x << ","<<y<< "\n";

}

 

doublearea()

{return (3.14159* radius *radius);};

 

protected:

doubleradius;

};

 

//note castingcircle to point

ostream &operator <<(ostream & os, Circle& aCircle)

{

 os<< "Circle:radius:" <<aCircle.radius;

 //os<< aCircle.x<<aCircle.y<<endl;

 os<< (Point&)aCircle <<"\n";          

 return os;     

}

 

//We will look at a few main programsbased on previous class definitions. Casting and assignments

voidmain (void )

{

 Point p(2,3);         cout <<"Point P=  "<< p;

 

 Point pp(0,0);       cout <<"Point PP=  "<< pp;

 

 Circle c(7,6,5);     cout <<"Circle c=  "<< c;        //radius =7

 

pp = p;             cout <<"Point PP=  "<< pp;    //built in assign =

 

//a circle is a member of the point class so assign a circle to a point.

 

 pp = c;           //legal; also assignment O.K.

 cout <<"Point PP=  "<< pp;

 

 pp= (Point) c;    // but better  use the cast

 cout <<"Point PP=  "<< pp;  //note we get only the point part of theCircle

 

 //c = (Circle) pp;   // illegal Cannot convert 'class Point' to 'class Circle'

 

 //c=pp;                 //illegal assignment notdefined

 

 Point* pPoint;

 pPoint = &c;

 

 pPoint ->print();    //call base class print

 ((Circle*) pPoint)->print();

 

//Circle* pc = &pp;

 

Point&r = c;

r.print();

((Circle&)r).print();

 

}

 

Output

PointP=   Point:X:Y: 2,3

PointPP=   Point:X:Y: 0,0

Circlec=  Circle:radius:7 Point:X:Y: 6,5

 

PointPP=   Point:X:Y: 2,3

PointPP=   Point:X:Y: 6,5

PointPP=   Point:X:Y: 6,5

 

Point:X:Y:2,3

Circle:radius:7Point:X:Y: 6,5

Point:X:Y:2,3

Circle:radius:7Point:X:Y: 6,5

在子类继承了父类之后,在构造函数中的参数列表了,应该加上继承了的父类成员,并且在初始化类表里调用父类的构造函数,在拷贝构造中也是一样,赋值运算符重载中也是一样,要调用父类的赋值运算符重载,在调用赋值运算符重载时,直接把子类赋值运算符重载函数参数当做父类赋值运算符重载的参数,这样有吧子类对象当做了父类对象来使用,这也仅仅是吧子类对象继承自父类对象的那部分成员赋值给父类。这个时候要记得实现三大件,也就是1)、拷贝构造,2)、析构函数,3)、赋值运算符重载,,在调用玩释放的时候是先调用子类的析构函数,然后再调用父类的构造函数。同时,父类的析构函数声明为虚(virtual)析构并不是什么坏事,相反会增加程序的健壮性(又称鲁棒性),因为如果不这样做的话,当析构一个指向new出来的子类对象的父类类型的指针时,则只会调用父类的析构函数,不会调用子类的析构函数,由于new是在堆上开辟空间,这个时候就不会释放,造成内存泄露。反而如果把父类的析构函数声明为virtual的话,这个时候就会调用子类的析构函数,而当调用了子类的析构之后,又会调用父类的析构函数,就会一举两得。

Eg:

Example 7:

#include <iostream.h>

#include <string.h>

class Base

{

protected:

int id;

char*  name;

public:

// default constructor

Base(int a = 0, char *s = "") : id(a)

{

if (!s)

{

name = NULL;

}

else

{

name =new char[strlen(s) + 1];

strcpy(name,s);

}

cout <<"base default constructor\n";

}

// copy constructor

Base(const Base& b): id(b.id)

{

if (!b.name) {name = NULL; }

else

{

name =new char[strlen(b.name) + 1];

strcpy(name, b.name);

}

cout << "base copyconstructor\n";

}

// destructor

~Base( )

{

if( name != NULL )    delete [ ] name;

cout <<"base destructor\n";

}

const Base&operator= (const Base& b);

friend ostream& operator << (ostream&, constBase&);

};

const Base& Base::operator= (const Base& b)

{

if (this != &b)                       // Check if an object isassigned to itself.

{

id = b.id;

delete [ ]name;          //  Destroy the old object.

if (!b.name) {name = NULL; }

else

{

name = newchar[strlen(b.name) + 1];

strcpy(name, b.name);

}

}

cout << "base assignment operator\n";

return *this;

}

ostream& operator << (ostream& out, const Base& b)

{

out << "Basemember id = " << b.id << endl;

out << "Basemember name = " << b.name << endl;

return out;

}

class Derived : public Base

{

private:

float f;

char* label;

public:

// default constructor

Derived(int a = 0, char* s = "", float x = 0, char * t = "") :
Base(a, s)
, f(x)

{

if (!t) { label= NULL; }

else

{

label = new char[strlen(t) + 1];

strcpy(label, t);

}

cout <<"derived default constructor\n";

}

// copy constructor

Derived(constDerived& d) : Base(d), f(d.f)

// d used as an instance of Base

{

if(!d.label) { label= NULL; }

else

{

label =new char [strlen(d.label) + 1];

strcpy(label, d.label);

}

cout <<"derived copy constructor\n";

}

// destructor

~Derived( )

{

delete [ ] label;

cout <<"derived destructor\n";

}

const Derived&operator= (const Derived& d);

friend ostream& operator << (ostream&, constDerived&);

};

const Derived& Derived::operator= (const Derived& d)

{

if (this != &d)

{

delete [ ]label;

Base::operator=(d); // Assign the Base part of d to the Base

// part of theobject that calls this operator;

//(Base)(*this)=(Base&)d;

f = d.f;

if (!d.label) { label = NULL; }

else

{

label = new char[strlen(d.label) + 1];

strcpy(label,d.label);

}

cout <<"derived assignment operator\n";

}

return *this;

}

ostream& operator << (ostream& out, const Derived&d)

{

out << (Base&)d;               // Convert d to Base object tooutput Base members.

out <<"Derived member f = " << d.f << endl;

out <<"Derived member label = " << d.label << endl;

return out;

}

int main( )

{

Derived d1;

Derived  d2(d1);

return 0;

}

The output of the program is:

base default constructor                   // constructs the base partof d1

derived default constructor     // constructs the additional part of d1

base copy constructor              // copy the base part of d1 to thebase part of d2

derived copy constructor         // copy the rest part of d1 to d2

derived destructor            // derived destructor called to destroy d2

多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。引用CharlieCalverts对多态的描述——多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4
编程技术内幕”)。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在Object Pascal和C++中都是通过虚函数(Virtual Function)实现的

封装,capsulation,&&继承,Inheritance,&&多态,polymorphism的更多相关文章

  1. JavaScript 定义类的最佳写法——完整支持面向对象(封装、继承、多态),兼容所有浏览器,支持用JSDuck生成文档

    作者: zyl910 [TOC] 一.缘由 由于在ES6之前,JavaScript中没有定义类(class)语法.导致大家用各种五花八门的办法来定义类,代码风格不统一.而且对于模拟面向对象的三大支柱& ...

  2. 黑马程序猿——JAVA面向对象的特性:封装,继承,多态

                                           - ----------android培训.java培训.java学习型技术博客.期待与您交流!------------  ...

  3. C++封装、继承、多态

    C++封装继承多态总结 面向对象的三个基本特征 面向对象的三个基本特征是:封装.继承.多态.其中,封装可以隐藏实现细节,使得代码模块化:继承可以扩展已存在的代码模块(类):它们的目的都是为了--代码重 ...

  4. OOP三个基本特征:封装、继承、多态

    面向对象的三个基本特征是:封装.继承.多态. 封装 封装最好理解了.封装是面向对象的特征之一,是对象和类概念的主要特性. 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类 ...

  5. OC的封装、继承与多态

    面向对象有三大特征:封装.继承和多态. 一.封装 封装是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问.简而言之,信息隐藏,隐 ...

  6. 2、C#面向对象:封装、继承、多态、String、集合、文件(上)

    面向对象封装 一.面向对象概念 面向过程:面向的是完成一件事情的过程,强调的是完成这件事情的动作. 面向对象:找个对象帮你完成这件事情. 二.面向对象封装 把方法进行封装,隐藏实现细节,外部直接调用. ...

  7. PHP面向对象三大特点学习(充分理解抽象、封装、继承、多态)

    PHP面向对象三大特点学习 学习目标:充分理解抽象.封装.继承.多态   面象对向的三大特点:封装性.继承性.多态性 首先简单理解一下抽象:我们在前面定义一个类的时候,实际上就是把一类事物共有的属性和 ...

  8. 初步理解Java的三大特性——封装、继承和多态

    声明:整理自网络,如有雷同,请联系博主处理 一.封装 封装从字面上来理解就是包装的意思,专业点就是信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被 ...

  9. 1.C#基础篇-->封装、继承和多态

    面向对象三要素:封装.继承和多态.正确理解这三个要素,才能在编程中建立面向对象的思想. 1.封装使用篇 作用:好的封装增加代码的可读性,易于维护. 什么情况下使用封装,封装的原则是? 1>功能相 ...

  10. C实现面向对象封装、继承、多态

    参考资料:      http://blog.chinaunix.net/uid-26750235-id-3102371.html      http://www.eventhelix.com/rea ...

随机推荐

  1. 【Python】Python-skier游戏[摘自.与孩子一起学编程]

    这是一个滑雪者的游戏. skier从上向下滑,途中会遇到树和旗子,捡起一个旗子得10分,碰到一颗树扣100分,可以用左右箭头控制skier方向. 准备素材 一 准备python环境:我下载的pytho ...

  2. lightoj 1015

    水题,统计大于0的和. #include<cstdio> int main(){ int t, n, tmp; scanf("%d", &t); for(int ...

  3. [selenium webdriver Java]处理弹出窗口

    Selenium WebDriver测试弹出窗口,包括识别弹出窗口,将driver转到新的窗口,在新的串钩中执行而是步骤,然后再转换到最初的窗口. 通过名称(name)识别和处理: Selenium ...

  4. REST API TESTING

    在敏捷开发过程中 每隔两周就是一个sprint,,, 在上个sprint中,任务就是REST API TESTING 因为以前没做过API 测试,不懂,然后经过询问查找 终于知道,需要发送请求,然后获 ...

  5. uvalive 4795 Paperweight

    题意:给出一个5个顶点的多面体以及多面体内一点P.求让 多面体不同的方式(即以不同的面)放在地面上,设这个着地的面为A,多面体重心在A上的投影为B,在保证B在A内部且距离A的各个边界不小于0.2的前提 ...

  6. Android--应用开发2(AndroidManfest.xml)

    AndroidManfest.xml 文件分析 manifest 根节点,描述package中所有内容 xmlns:android 包含命名空间声明.xmlns:android="http: ...

  7. 天天动听MP3解码器性能提升50%

    天天动听今日升级提醒,发现有一句 “使用新的MP3解码器,性能提升50%”,太惊讶了. 之前版本的MP3解码器使用libmpg123,效果已经是MP3解码器中非常不错的了. 50%的提升,应该不仅仅是 ...

  8. hdu4435-charge-station(搜索+贪心)

    题意&题解: http://www.cnblogs.com/wuminye/p/3245546.html 说实话看了题解觉得很简单,但是比赛的时候真的是毫无头绪. 然而印象中做过一道类似的二进 ...

  9. Objective-C中的Block

    1.相关概念 在这篇笔记开始之前,我们需要对以下概念有所了解. 1.1 操作系统中的栈和堆 注:这里所说的堆和栈与数据结构中的堆和栈不是一回事. 我们先来看看一个由C/C++/OBJC编译的程序占用内 ...

  10. 转载c#泛型 类型参数的约束(c#编程指南)

    在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制.如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误.这些限制称为约束.约束是使用 where 上 ...