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. iOS程序性能优化

    iOS程序性能优化 一.初级 使用ARC进行内存管理 在iOS5发布的ARC,它解决了最常见的内存泄露问题.但是值得注意的是,ARC并不能避免所有的内存泄露.使用ARC之后,工程中可能还会有内存泄露, ...

  2. 【译】 AWK教程指南 附录C-AWK的内建函数

    C.1 字串函数 index( 原字串, 查找的子字串 ) 若原字串中含有欲寻找的子字串,则返回该子字串在原字串中第一次出现的位置,若未曾出现该子字串则返回0. 例如: $ awk 'BEGIN{ p ...

  3. MFC文档、视图和框架

    文档.视图.框架 文档/视图结构是MFC提供的一种不错的设计,它将数据的处理和显示分开来,这样更便于我们对程序的维护和扩展. 文档        文档对象用于管理和维护数据,包括保存数据.取出数据以及 ...

  4. Windows内核编程之:分页内存与非分页内存 #define PAGEDCODE code_seg("PAGE") (转)

    原文链接:http://blog.chinaunix.net/uid-24504987-id-161192.html Windows规定有些虚拟内存可以交换到文件中,这类内存被称为分页内存 有些虚拟内 ...

  5. [九度OJ]1078.二叉树的遍历(重建)

    原题链接:http://ac.jobdu.com/problem.php?pid=1078 题目描述: 二叉树的前序.中序.后序遍历的定义:前序遍历:对任一子树,先访问跟,然后遍历其左子树,最后遍历其 ...

  6. 进程隐藏与进程保护(SSDT Hook 实现)(二)

    文章目录:                   1. 引子 – Demo 实现效果: 2. 进程隐藏与进程保护概念: 3. SSDT Hook 框架搭建: 4. Ring0 实现进程隐藏: 5. Ri ...

  7. 决策树学习(ID3)

    参考:<机器学习实战> 优点:计算复杂度不高, 输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特 征数据. 缺点:可能会产生过度匹配问题. 适用数据类型:数值型和标称型. 创建分支 ...

  8. 使用list和tuple

    list Python内置的一种数据类型是列表:list.list是一种有序的集合,可以随时添加和删除其中的元素. 比如,列出班里所有同学的名字,就可以用一个list表示: >>> ...

  9. [OC Foundation框架 - 12] NSNumber

    1.概念 NSArray,NSDictionary只能放OC对象,不能放入基本数据类型 必须使用包装类NSNumber,把基本数据类型包装成OC对象 不支持自动包装解包   void number() ...

  10. .NET设计模式(1):开篇

    转载:http://terrylee.cnblogs.com/archive/2005/12/09/293465.html .NET设计模式开篇 --.NET设计模式系列之一 Terrylee,200 ...