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. PLSQL Develop 配置

    plsq 连接oracle数据库的配置步骤: 1.下载oracle客户端: 下载地址:http://www.oracle.com/technetwork/topics/winx64soft-08954 ...

  2. C++ 模板类解析

    具体模板类作用这边就不细说了,下面主要是描述下模板类的使用方法以及注意的一些东西. #include <iostream> using namespace std; template &l ...

  3. 深入浅出 JavaScript 对象 v0.5

    JavaScript 没有类的概念,因此它的对象与基于类的语言中的对象有所不同.笔者主要参考<JS 高级程序设计>.<JS 权威指南>和<JS 精粹> 本文由浅入深 ...

  4. 深入理解Linux的系统调用【转】

    一. 什么是系统调用 在Linux的世界里,我们经常会遇到系统调用这一术语,所谓系统调用,就是内核提供的.功能十分强大的一系列的函数.这些系统调用是在内核中实现的,再通过一定的方式把系统调用给用户,一 ...

  5. hadoop cdh 4.5的安装配置

    春节前用的shark,是从github下载的源码,自己编译.shark的master源码仅支持hive 0.9,支持hive 0.11的shark只是个分支,不稳定,官方没有发布release版,在使 ...

  6. Esper系列(十四)Contained-Event Selection

    功能:该语法是针对所查询事件中的属性又是另一种属性的查询结果控制. 格式: 1  "+j); 19      bean.setBean(item); 20      list.add(bea ...

  7. POJ 3648-Wedding(2-SAT)

    题面很邪恶啊... 一对新人请n-1对夫妻吃饭,人们坐在一张桌子的两侧,每一对互为夫妻关系的人必须坐在桌子的两侧.而且有些人两两之间会存在“通奸”关系,通奸关系不仅在男女之间,同性之间也有.新娘对面不 ...

  8. Android实例-操作sqlite数据库之Grid显示图片(XE8+小米2)

    结果: 1.数据库文件,记得打包到程序中(assets\internal\). 操作方法: 1.新建firemonkey mobile application①菜单->File->New- ...

  9. 教程-Delphi各版本与工具下载地址

    1.Delphi 7.0 下载地址:http://www.skycn.com/soft/2121.html 注册码:(正在用的没有问题)V8S3-KM82KQ-XN8JQK-EPS33EA-GZK汉化 ...

  10. Arrays.asList的源码分析

    以前一直很奇怪为什么Arrays.asList的数组不能插入新的数据,后来看了源码发现是因为内部是一个final的数组支持起来的Arraylist,下面贴入源码与分析. 1.先看Arrays的方法 我 ...