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. 15、NDK开发初步

    一.什么是NDK? NDK是Android让你潜入原生组件(C/C++开发)的一套开发套件 Android应用程序是运行在Dalvik虚拟机中的 ,NDK允许你通过原生代码实现部分的应用程序模块 . ...

  2. 【JMeter】JMeter完成一个MySql压力测试

    jmeter也可以用来做数据库的压力测试,并且兼容各种数据库类型,只需要更改对应的数据库驱动类和url.以下为整理到的数据库驱动类对应url.并且给出一个mysql数据库select的简单应用.如下: ...

  3. bzoj 1034 [ZJOI2008]泡泡堂BNB(贪心)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1034 [题意] 给两个序列以任意顺序比较,求出最大和最小得分. [思路] 排序后使用贪 ...

  4. uvalive 4992 Jungle Outpost

    题意:一个凸边型,目标在凸边型内且最优.问最多删除几个点使目标暴露在新凸边型外面. 思路:二分+半平面相交. #include<cstdio> #include<cmath> ...

  5. AVD Snapshot功能

    写程序的时候,经常会碰到:The application has stopped unexpectly… 有时候,会想对原来软件增加新功能或者修改bug.在eclipse修改后保存代码(注意,要保存所 ...

  6. HDU 1890--Robotic Sort(Splay Tree)

    题意:每次找出第i大的数的位置p输出,然后将i~p之间的数反转. 题解:每次把要的区间转成一棵子树,然后更新.因为每次将第i小的数转到了了i,所以k次操作后,可知前k个数一定是最小的那k个数,所以以后 ...

  7. Java TreeMap 源码解析

    继上篇文章介绍完了HashMap,这篇文章开始介绍Map系列另一个比较重要的类TreeMap. 大家也许能感觉到,网络上介绍HashMap的文章比较多,但是介绍TreeMap反而不那么多,这里面是有原 ...

  8. Altium Designer生成网表 导出网表【worldsing笔记】

    Design -> Netlist for project -> Protel

  9. C++ vector的用法

    其实我是一个比较懒惰的人 我最喜欢的循环方式是for(int i=0;i<length;i++) 同样也可以 for (unsigned i=0; i<sz; i++) myvector[ ...

  10. 我眼中的PM

    我眼中的PM 1 我眼中的PM 1.1 人云“一个管理,半个专家”,我说“一个管理,两个专家” 如今,我发现我们不得不面对这样一个现实——角色兼职.我习惯上把项目分为三类:性命攸关的项目(涉及到人身安 ...