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的编程规范),不建议使用私有继承和保护继承,而是使用组合方式。
//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<< " ";
}
protected: // accessed by derived class
double x;
double y;
};
ostream &operator << (ostream & os, Point & apoint)
{
os <<" Point:X:Y: "<<apoint.x <<","<< apoint.y<< " ";
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<< " ";
}
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 <<" ";
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 ";
}
// 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 ";
}
// destructor
~Base( )
{
if( name != NULL ) delete [ ] name;
cout <<"base destructor ";
}
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 ";
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 ";
}
// 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 ";
}
// destructor
~Derived( )
{
delete [ ] label;
cout <<"derived destructor ";
}
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 ";
}
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)实现的