一,基础知识
1,继承的含义:
在已有类的基础上创建新类的过程;
一个son类继承dade类,或称从类dade派生类 son,类dad称为父类(基类),类son称为子类(派生类)
2,继承的作用:
代码重用,使程序功能简洁;
3,基本用法
1》语法形式
类继承关系的语法形式
class 派生类名 : 基类名表
{
数据成员和成员函数声明
};
//基类名表:访问控制 基类名1, 访问控制 基类名2,… , 访问控制 基类名n
2》访问控制
public :公有继承
private:私有继承
protected:保护继承
4,子类(派生类)的形成过程
不论种方式继承基类,子类(派生类)都不能直接使用父类(基类)的私有成员
1》吸收父类(基类)成员:
全部吸收(构造、析构除外),但不一定可见
2》改造父类(基类)成员
通过在子类(派生类)中定义同名成员(包括成员函数和数据成员)来屏蔽(隐藏)在子类(派生类)中不起作用的部分父类(基类)成员。
3》添加新成员:
仅仅继承父类(基类)的成员是不够的,需要在派生类中添加新成员,以保证派生类自身特殊属性和行为的实现。
5,子类(派生类)的对象的内容
1》屏蔽重名成员
在派生类中访问同名成员时屏蔽(hide)了父类(基类)的同名成员
在派生类中使用父类(基类)的同名成员,显式地使用类名限定符:
类名 :: 成员
2》子类(派生类)中访问静态成员
父类(基类)定义的静态成员,将被所有派生类共享(父类和派生类共享父类的静态成员)
根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质
类名 :: 成员
//或通过对象访问
对象名 . 成员
6,父类(基类的初始化)
在创建派生类对象时用指定参数调用父类(基类)的构造函数来初始化派生类继承父类(基类)的数据
派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 )
… 对象成员n ( 变元表 ) ;
7,子类(派生类)的构造函数
如果派生类的父类(基类)也是派生类,则每个派生类只负责直接父类(基类)的构造
派生类是否定义析构函数与所属的父类(基类)无关
//在C++中,派生类构造函数的一般格式为:
派生类::派生类名(参数总表):基类名(参数表)
{
// 派生类新增成员的初始化语句
}
//注意:这是父类(基类)有构造函数且含有参数时使用
8
,子类(派生类)的析构函数
父类(基类)的构造函数和析构函数不能被继承
如果父类(基类)没有定义构造函数或有无参的构造函数, 派生类也可以不用定义构造函数
如果父类(基类)无无参的构造函数,派生类必须定义构造函数
1)当派生类中不含对象成员时
在创建派生类对象时,构造函数的执行顺序是:父类(基类)的构造函数→派生类的构造函数;
在撤消派生类对象时,析构函数的执行顺序是:派生类的析构函数→父类(基类)的析构函数。
(2)当派生类中含有对象成员时
在定义派生类对象时,构造函数的执行顺序:父类(基类)的构造函数→对象成员的构造函数→派生类的构造函数;
在撤消派生类对象时,析构函数的执行顺序:派生类的析构函数→对象成员的析构函数→父类(基类)的析构函数。
9,多继承
1》多继承是什么: 一个类有多个直接父类(基类)的继承关系称为多继承
2》声明语法
class 派生类名 : 访问控制 基类名1 , 访问控制 基类名2 , … , 访问控制 基类名n
{
数据成员和成员函数声明
};
3》多继承的构造函数
多个父类(基类)的派生类构造函数可以用初始式调用父类构造函数初始化数据成员。
执行顺序与单继承构造函数情况类似。多个直接父类构造函数执行顺序取决于定义派生类时指定的各个继承父类(基类)的顺序。
一个派生类对象拥有多个直接或间接父类的成员。不同名成员访问不会出现二义性。如果不同的父类(基类)有同名成员,派生类对象访问时应该加以识别。
派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),…,基类名n(参数表n)
{
// 派生类新增成员的初始化语句
}
1-构造函数的执行顺序
a,先执行所有父类(基类)的构造函数,再执行对象成员的构造函数,最后执行派生类的构造函数
b, 处于同一层次的各父类(基类)构造函数的执行顺序取决于定义派生类时所指定的父类(基类)顺序,与派生类构造函数中所定义的成员初始化列表顺序没有关系。
c,内嵌对象成员的构造函数执行顺序与对象在派生类中声明的顺序一致
4》多继承的析构函数
1-析构函数名同样与类名相同,无返回值、无参数,而且其定义方式与父类(基类)中的析构函数的定义方式完全相同。
2-析构函数的功能是在派生类中对新增的有关成员进行必要的清理工作。
3-析构函数的执行顺序与多继承方式下构造函数的执行顺序完全相反,首先对派生类新增的数据成员进行清理,再对派生类对象成员进行清理,最后才对父类(基类)继承来的成员进行清理。
10,赋值兼容规则
1》赋值兼容规则中所指的替代包括以下的情况:
1-派生类的对象可以赋给父类(基类)对象
2-派生类的对象可以初始化父类(基类)的引用
3-派生类的对象的地址可以赋给父类(基类)类型的指针
ps:赋值兼容规则指在程序中需要使用父类(基类)对象的任何地方,都可以用公有派生类的对象来替代。
二,实际应用
1,简单的继承:
class Father
{
int a,b;
public:
// 成员函数
};
class Son:public Father//(son类继承于father类)
{
int c;
public:
// 成员函数
};
2,公有继承
#include<iostream>
using namespace std ;
class A
{ public :
void get_XY() { cout << "Enter two numbers of x, y : " ; cin >> x >> y ; }
void put_XY() { cout << "x = "<< x << ", y = " << y << '\n' ; }
protected: int x, y ;
};
class B : public A
{ public :
int get_S() { return s ; };
void make_S() { s = x * y ; }; // 使用基类数据成员x,y
protected: int s;
};
class C : public B
{ public :
void get_H() { cout << "Enter a number of h : " ; cin >> h ; }
int get_V() { return v ; }
void make_V() { make_S(); v = get_S() * h ; } // 使用基类成员函数
protected: int h, v;
};
int main()
{ A objA ;
B objB ;
C objC ;
cout << "It is object_A :\n" ;
objA.get_XY() ;
objA.put_XY() ;
cout << "It is object_B :\n" ;
objB.get_XY() ;
objB.make_S() ;
cout << "S = " << objB.get_S() << endl ;
cout << "It is object_C :\n" ;
objC.get_XY() ;
objC.get_H();
objC.make_V() ;
cout << "V = " << objC.get_V() << endl ;
}
3,子类(派生类)与父类(基类)的简单应用
#include<iostream>
#include <string>
using namespace std;
class Person
{
string name;
int age;
string sex;
public:
void set_p() {
cout<<"name\tage\tsex"<<endl;
cin>>name>>age>>sex;
}
void show_p() {
cout<<name<<" "<<age<<" "<<sex<<endl;
}
};
class student :public Person
{
string no;
string zhuanye;
string t_class;
float score;
public:
void set_t(){
set_p(); //调用继承于基类的成员函数访问继承于基类的私有数据成员
cout<<"zhuanye\tt_class\tscore"<<endl;
cin>>zhuanye>>t_class>>score;
}
void show_t() {
show_p();
cout<<zhuanye<<" "<<t_class<<" "<<score<<endl;
}
};
4,子类中的重名成员
class base
{ public :
int a , b ;
} ;
class derived : public base
{ public :
int b , c ;
} ;
void f ()
{ derived d ;
d . a = 1 ;
d . base :: b = 2 ;
d . b = 3 ;
d . c = 4 ;
};
#include<iostream>
usingnamespace std ;
classA
{public:
int a1, a2 ;
A( int i1=0, int i2=0 ) { a1 = i1; a2 =i2; }
voidprint()
{ cout << "a1=" << a1 << '\t' <<"a2=" << a2 << endl ; }
};
classB : public A
{public:
int b1, b2 ;
B( int j1=1, int j2=1 ) { b1 = j1; b2 =j2; }
void print() //定义同名函数
{ cout << "b1=" << b1 << '\t' <<"b2=" << b2 << endl ; }
5,继承的实例(点,线,圆)
class Point
{ friend ostream &operator<< (ostream &, const Point &);
public:
Point( int = 0, int = 0 ) ; // 带默认参数的构造函数
void setPoint( int, int ) ; // 对点坐标数据赋值
int getX() const { return x ; } int getY() const { return y ; }
protected: int x, y; // Point类的数据成员
};
class Circle : public Point
{ friend ostream &operator<< (ostream &, const Circle &); // 友元函数
public:
Circle(double r=0.0, int x=0, int y=0); // 构造函数
void setRadius(double); /*置半径*/ double getRadius() const; /*返回半径*/
double area() const; // 返回面积
protected: double radius; // 数据成员,半径
};
class Cylinder:public Circle
{ friend ostream & operator<<(ostream &, const Cylinder &); // 友元函数
public:
Cylinder(double h=0.0, double r=0.0, int x=0, int y=0); // 构造函数
void setHeight(double); /* 置高度值*/ double getHeight() const; /* 返回高度值*/
double area() const; /* 返回面积*/ double volume() const; /* 返回体积*/
protected: double height; // 数据成员,高度
};
// Point 类的成员函数
// 构造函数,调用成员函数对 x,y作初始化
Point::Point ( int a, int b )
{ setPoint ( a , b ) ; }
// 对数据成员置值
void Point :: setPoint ( int a, int b ) { x = a ; y = b ; }
// 重载插入算符,输出对象数据
ostream &operator<< ( ostream &output , const Point &p )
{ output << '[' << p.x << "," << p.y << "]" ;
return output ;
}
// Circle 类的成员函数
// 带初始化式构造函数,首先调用基类构造函数
Circle::Circle( double r, int a, int b ): Point( a, b ) { setRadius ( r ); }
// 对半径置值
void Circle::setRadius ( double r ) { radius = ( r >= 0 ? r : 0 ); }
// 返回半径值
double Circle::getRadius() const { return radius; }
// 计算并返回面积值
double Circle::area() const { return 3.14159 * radius * radius ; }
// 输出圆心坐标和半径值
ostream & operator<< ( ostream &output, const Circle &c)
{ output << "Center = " << '[' << c.x << "," << c.y << "]" << "; Radius = "
<< setiosflags(ios::fixed|ios::showpoint) << setprecision(2) << c.radius ;
return output ;
}
Cylinder::Cylinder(double h, double r, int x, int y):Circle(r,x,y) { setHeight(h); }
void Cylinder::setHeight(double h) { height = ( h >= 0 ? h : 0 ); }
double Cylinder::getHeight() const { return height; }
double Cylinder::area()const{ return 2*Circle::area()+2*3.14159*radius*height; }
double Cylinder::volume() const { return Circle::area()*height; }
ostream &operator<< ( ostream &output, const Cylinder &cy )
{ output << "Center = " << '[' << cy.x << "," << cy.y << "]" << "; Radius = "
<< setiosflags(ios::fixed|ios::showpoint) << setprecision(2) << cy.radius
<< "; Height = " << cy.height << endl ;
return output;
}
#include<iostream>
using namespace std ;
#include <iomanip.h>
int main(){
Point p ( 72, 115 ) ; //定义点对象并初始化
cout << "The initial location of p is " << p << endl ;
p.setPoint ( 10, 10 ) ; //置点的新数据值
cout << "\nThe new location of p is " << p << endl ; //输出数据
Circle c ( 2.5, 37, 43 ) ; //定义圆对象并初始化
cout<<"\nThe initial location and radius of c are\n"<<c<<"\nArea = "<<c.area()<<"\n" ;
c.setRadius ( 4.25 ) ; c.setPoint ( 2, 2 ) ;
cout<<"\nThe new location and radius of c are\n"<<c<<"\nArea = "<<c.area()<< "\n" ;
Cylinder cyl ( 5.7, 2.5, 12, 23 ) ; //定义圆柱体对象并初始化
cout << "\nThe initial location, radius ang height of cyl are\n" << cyl
<< "Area = " << cyl.area() << "\nVolume = " << cyl.volume() << '\n';
cyl.setHeight ( 10 ) ; cyl.setRadius ( 4.25 ) ; cyl.setPoint ( 2, 2 ) ;
cout << "\nThe new location, radius ang height of cyl are\n" << cyl
<< "Area = " << cyl.area() << "\nVolume = "<<cyl.volume()<< "\n" ;
}
6,多继承
class Base1
{ public:
Base1(int x) { value = x ; }
int getData() const { return value ; }
protected:
int value;
};
class Base2
{ public:
Base2(char c) { letter=c; }
char getData() const { return letter;}
protected:
char letter;
};
class Derived : public Base1, public Base2
{ friend ostream &operator<< ( ostream &, const Derived & ) ;
public :
Derived ( int, char, double ) ;
double getReal() const ;
private :
double real ;
};
int main()
{ Base1 b1 ( 10 ) ;
Base2 b2 ( 'k' ) ;
Derived d ( 5, 'A', 2.5 ) ;
return ;
}
三,学习感悟
继承这一方面感觉还是蛮实用的,一开始学的时候觉得概念好多好麻烦,真正学进去后觉得还是挺简单的,有些概念基本上都用不到,多继承如果多个父类里面有重名成员的话会有歧义,不是特别方便,实际应用中也没怎么用到。一开始想着代码越长越厉害,学着学着才发现,同样的事,代码越短才是越厉害,而继承这一块的内容,恰恰实现了这一功能,使相同代码可以重用,不但减少了工作量,而且也使类与类之间的关系更加清晰明了,谁包含谁,什么功能应该在哪里实现。有点遗憾的地方是,在进行课程设计的时候,没有想到会在子类里面重载函数,函数名来回引用之间有些混乱,下次会把待继承的父类写的“有特色”一点,让类与类之间的关系更加明了,功能分配也更加清楚明白。