1. 继承和派生类概念
继承性是面向对象程序设计中最重要的机制。这种机制提供了无限重复利用程序资源的一种途径。通过C++语言中的继承机制,可以扩充和完善旧的程序设计以适应新的需求。这样不仅可以节省程序开发的时间和资源,并且为未来程序增添了新的资源。
class Student
{ int num;
char name[30];
char sex;
public:
void display( ) //对成员函数display的定义
{cout<<"num: "<<num<<endl;
cout<<"name: "<<name<<endl;
cout<<"sex: "<<sex<<endl; }
};
|
class Studend1
{ int num; //此行原来己有
char name[20]; //此行原来己有
char sex; //此行原来己有
int age;
char addr[20];
public:
void display( ) ; //此行原来己有
{cout<<"num: "<<num<<endl; //此行原来己有
cout<<"name: "<<name<<endl;//此行原来己有
cout<<"sex: "<<sex<<endl; //此行原来己有
cout<<"age: "<<age<<endl;
cout<<"address: "<<addr<<endl;}
};
|
利用原来定义的类Student作为基础,再加上新的内容即可,以减少重复的工作量。 C++提供的继承机制就是为了解决这个问题。在C++中所谓“继承”就是在一个已存在的类的基础上建立一个新的类。已存在的类称为“基类(base class)”或“父类(father class)”。新建立的类称为“派生类(derived class)”或“子类(son class)”。
class Student1: public Student//声明基类是Student
{private:
int age; //新增加的数据成员
string addr; //新增加的数据成员
public:
void display_1( ) //新增加的成员函数
{ cout<<"age: "<<age<<endl;
cout<<"address: "<<addr<<endl;
}
};
|
类A派生类B:类A为基类,类B为派生类。
在C++语言中,一个派生类可以从一个基类派生,也可以从多个基类派生。从一个基类派生的继承称为单继承;从多个基类派生的继承称为多继承。
通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员。我们称已存在的用来派生新类的类为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。
在建立派生类的过程中,基类不会做任何改变,派生类则除了继承基类的所有可引用的成员变量和成员函数外,还可另外定义本身的成员变量和处理这些变量的函数,由于派生类可继承基类的成员变量和成员函数,因此在基类中定义好的数据和函数等的程序代码可重复使用,这样可以提高程序的可靠性。
当从已有的类中派生出新的类时,可以对派生类做以下几种变化:
1、 可以继承基类的成员数据或成员函数。
2、可以增加新的成员变量。
3、可以增加新的成员函数。
4、可以重新定义已有的成员函数。
5、可以改变现有的成员属性。
在C++中有二种继承:单一继承和多重继承。当一个派生类仅由一个基类派生时,称为单一继承;而当一个派生类由二个或更多个基类所派生时,称为多重继承。
但派生并不是简单的扩充,有可能改变基类的性质。
有三种派生方式:公有派生、保护派生、私有派生。
默认的是私有派生。
从一个基类派生一个类的一般格式为:
class ClassName:BaseClassName
{
private:
......; //私有成员说明
public:
......; //公有成员说明
protected:
......; //保护成员说明
}
基类:
public: 在派生类和类外可以使用
protected: 在派生类中使用
private: 不能在派生类中使用
class A { int x;
protected: int y;
public: int z;
A(int a,int b,int c){x=a;y=b;z=c;}//基类初始化
int Getx(){return x;} //返回x
int Gety(){return y;} //返回y
void ShowA(){cout<< "x="<<x<<'\t'<<"y="<<y<<'\t'<<"z="<<z<<'\n';}
};
class B:public A{
int m,n;
public: B(int a,int b,int c,int d,int e):A(a,b,c){m=d;n=e;}
void Show(){cout<<“m="<<<n<<'\n';
cout<<"x="<<Getx()<<'\t'<<"y="<<y<<'\t'<<"z="<<z<<'\n'; }
int Sum(){return ( Getx()+y+z+m+n);}
};
void main(void)
{ B b1(1,2,3,4,5);
b1.ShowA(); b1.Show();
cout<< "Sum="<<b1.Sum()<<'\n';cout<<"x="<<b1.Getx()<<'\t';
cout << "y=" <<b1.Gety()<<'\t'; cout << "z="<<b1.z<<'\n';}
|
基类:
public: (变为私有)在派生类中使用,类外不可使用
protected: (变为私有)在派生类中使用,类外不可使用
private: 不能在派生类中和类外使用
class A { int x;
protected: int y;
public: int z;
A(int a,int b,int c){x=a;y=b;z=c;}//基类初始化
int Getx(){return x;} //返回x
int Gety(){return y;} //返回y
void ShowA(){cout<< "x="<<x<<'\t'<<"y="<<y<<'\t'<<"z="<<z<<'\n';}
};
class B:private A{
int m,n;
public: B(int a,int b,int c,int d,int e):A(a,b,c){m=d;n=e;}
void Show(){cout<<“m="<<<n<<'\n';
cout<<"x="<<Getx()<<'\t'<<"y="<<y<<'\t'<<"z="<<z<<'\n'; }
int Sum(){return ( Getx()+y+z+m+n);}
};
void main(void)
{ B b1(1,2,3,4,5);
b1.ShowA(); b1.Show();
cout<< "Sum="<<b1.Sum()<<'\n';cout<<"x="<<b1.Getx()<<'\t';
cout << "y=" <<b1.Gety()<<'\t'; cout << "z="<<b1.z<<'\n';}
|
基类:
public: (变为保护)在派生类中使用,类外不可使用
protected: (不变)在派生类中使用,类外不可使用
private: 不能在派生类中和类外使用
protected 成员是一种具有血缘关系内外有别的成员。它对派生类的对象而言,是公开成员,可以访问,对血缘外部而言,与私有成员一样被隐蔽。
抽象类与保护的成员函数
当定义了一个类,这个类只能用作基类来派生出新的类,而不能用这种类来定义对象时,称这种类为抽象类。当对某些特殊的对象要进行很好地封装时,需要定义抽象类。
将类的构造函数或析构函数的访问权限定义为保护的时,这种类为抽象类。
当把类中的构造函数或析构函数说明为私有的时,所定义的类通常是没有任何实用意义的,一般情况下,不能用它来产生对象,也不能用它来产生派生类。
在类B中不能定义A的对象
在任何时候都不能定义A的对象
但可以在初始化类B的对象时初始化原类A中的成员,因为A()在类B中是可以被调用的。
class A { int x, y;
protected: A(int a,int b){x=a;y=b;}//基类初始化
public:
void ShowA(){cout<< "x="<
};
class B: public A{
int m;
A a1; //在派生类中也不可以定义A的对象,实际上还是类外调用
public:
B(int a,int b,int c):A(a,b)//可以在派生类中调用A的构造函数
{m=c;}
void Show(){ cout<<“m="<
};
void main(void)
{ B b1(1,2,3); //可以定义派生类对象
b1.Show();
A aa; //不可定义A的对象 }
格式为:
class 类名:类名1,..., 类名n
{
private: ...... ; //私有成员说明;
public: ...... ; //公有成员说明;
protected: ...... ; //保护的成员说明;
};
class D: public A, protected B, private C
{ ....//派生类中新增加成员
};
class A{ int x1,y1;
public: A(int a,int b) { x1=a; y1=b; }
void ShowA(void){ cout<<"A.x="<<x1<<'\t'<<"A.y="<<y1<<endl; }
};
class B{int x2,y2;
public: B(int a,int b) {x2=a; y2=b; }
void ShowB(void){ cout<<"B.x="<<x2<<'\t'<<"B.y="<<y2<<endl; }
};
class C:public A,private B{
int x,y;
public: C(int a,int b,int c,int d,int e,int f):A(a,b),B(c,d) {x=e; y=f; }
void ShowC(void){cout<<"C.x="<<x<<'\t'<<"C.y="<<y<<endl;
ShowA();ShowB(); }
};
void main(void)
{ C c(1,2,3,4,5,6);
c.ShowC();
c.ShowA ();
c.ShowB (); //非法调用
}
|
初始化基类成员
构造函数不能被继承,派生类的构造函数必须调用基类的构造函数来初始化基类成员基类子对象。
派生类构造函数的调用顺序如下:
基类的构造函数
子对象类的构造函数
派生类的构造函数
class Base1 { int x;
public: Base1(int a){ x=a;cout<<"调用基类1的构造函数!\n"; }
~Base1( ){ cout<<"调用基类1的析构函数!\n"; }
};
class Base2 { int y;
public: Base2(int a){ y=a;cout<<"调用基类2的构造函数!\n"; }
~Base2( ){ cout<<"调用基类2的析构函数!\n"; }
};
class Derived:public Base2, public Base1{
int z;
public: Derived(int a,int b):Base1(a),Base2(20)
{z=b; cout<<"调用派生类的构造函数!\n";}
~Derived( ){cout<<"调用派生类的析构函数!\n";}
};
void main(void)
{ Derived c(100,200);
}
|
基类与对象成员
任一基类在派生类中只能继承一次,否则,会造成成员名的冲突。若在派生类中,确实要有二个以上基类的成员,则可用基类的二个对象作为派生类的成员。把一个类作为派生类的基类或把一个类的对象作为一个类的成员,在使用上是有区别的:在派生类中可直接使用基类的成员(访问权限允许的话),但要使用对象成员的成员时,必须在对象名后加上成员运算符“.”和成员名。
在平面上作两个点,连一直线,求直线的长度和直线中点的坐标。
基类为Dot,有两个公有数据成员,即平面上的坐标(x,y),同时有构造函数及打印函数。
派生类为Line,有两个基类Dot对象,分别存放两点的坐标,同时,从基类继承了一个Dot数据,存放直线中点的坐标。
class Dot{
public: float x,y;
Dot(float a=0,float b=0){ x=a; y=b;}
Dot(&dot)
void Show(void){cout<<"x="<<x<<'\t'<<"y="<<y<<endl;}
};
class Line:public Dot{
Dot d1,d2;
public: Line(Dot dot1,Dot dot2):d1(dot1),d2(dot2)
{ x=(d1.x+d2.x)/2; y=(d1.x+d2.y)/2;}
void Showl(void){ cout<<"Dot1: "; d1.Show(); cout<<"Dot2: "; d2.Show();
cout<<"Length="<<sqrt((d1.x-d2.x)*(d1.x-d2.x)+(d1.y-d2.y)*(d1.y-d2.y))<<endl;
cout<<"Center: "<<"x="<<x<<'\t'<<"y="<<y<<endl; }
};
void main(void)
{ float a,b;
cout<<"Input Dot1: \n"; cin>>a>>b;
Dot dot1(a,b);//调用Dot的构造函数
cout<<"Input Dot2: \n"; cin>>a>>b;
Dot dot2(a,b); Line line(dot1,dot2); line.Showl();}
|
可以将派生类对象的值赋给基类对象。反之不行
只是将从基类继承来的成员赋值。
可以将一个派生类对象的地址赋给基类的指针变量。
派生类对象可以初始化基类的引用。
class A{
public: int x;
A(int a=0) { x=a;}
};
class B{
public: int y;
B(int a=0) { y=a;}
};
class C:public A,public B{ int z;
public: C(int a,int b,int m): A(a), B(b) { z=m; }
void Show( ){ cout<<"x="<<x<<'\t'; cout<<"y="<<y<<‘\t’;
cout<<"z="<<z<<'\n'; }
};
void main(void)
{ A a1(100); B b1(200); C c1(10,20,50);
cout<<"a1.x="<<a1.x<<endl; cout<<"b1.y="<<b1.y<<endl;
c1.Show(); a1=c1; b1=c1;//派生类对象向基类对象赋值
cout<<"a1.x="<<a1.x<<endl; cout<<"b1.y="<<b1.y<<endl;
A *p; p=&c1; //基类指针指向派生类对象 p->Show();
}
|
在多重派生的过程中,若使公共基类在派生类中只有一个拷贝,则可将这种基类说明为。
在派生类的定义中,只要在基类的类名前加上关键字virtual,就可以将基类说明为虚基类。
class B:public virtual A{
public:
int y;
B(int a=0, int b=0 ):A(b) { y=a;}
};
|
再次强调,用虚基类进行多重派生时,若虚基类没有缺省的构造函数,则在每一个派生类的构造函数中都必须有对虚基类构造函数的调用 (且首先调用)。
虚析构函数:在析构函数前加关键字virtual进行说明,则该析构函数称为虚析构函数;
如果一个类的析构函数被说明为虚析构函数,则它的派生类中的析构函数也是虚析构函数,不管它是否使用了关键字virtual进行说明;
目的:
使用delete运算符删除一个对像时,能保证析构函数被正确地执行;
格式:
class B
{
public:
virtual ~B();
……..
}
阅读(976) | 评论(0) | 转发(0) |