中国大学MOOC程序设计与算法(三):C++ 笔记:继承

继承和派生

继承和派生的概念

继承:在定义一个新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个 基类,而把B作为基类的一个 派生类(也称子类) 。

  • 派生类是通过对基类进行修改和扩充得到的。在派生类中,可以扩充新的成员变量和成员函数。
  • 派生类一经定义后,可以独立使用,不依赖于基类。
  • 派生类拥有基类的全部成员函数和成员变量,不论是private、protected、public 。
  • 在派生类的各个成员函数中,不能访问基类中的private成员

实例

class CPoint
{
    private:
        double x, y;
    public:                           
        CPoint(double tx, double ty);
        CPoint (CPoint & p);
        virtual double GetArea() = 0;         
        virtual double GetVolum () = 0;
        virtual void print ();
} ;
CPoint :: CPoint(double tx, double ty) : x(tx), y(ty) {} 
CPoint :: CPoint(CPoint & p) : x(p.x), y(p.y) {}

void CPoint :: print ()
{
    cout << "Center:(" << x << "," << y << ")\n";
}
class CCircle : public CPoint
{
    private:
        double radius;
    public:
        CCircle(double tx, double ty, double r);
        CCircle(CCircle & c);
        virtual double GetArea();
        virtual double GetVolum () = 0;
        virtual void print ();
};

CCircle :: CCircle(double tx, double ty, double r) :CPoint(tx, ty), radius(r) {}
CCircle::CCircle(CCircle & c) : CPoint :: CPoint(c), radius (c.radius){}

double CCircle :: GetArea()
{
    return PI * radius * radius;
}

void CCircle :: print ()
{
    CPoint :: print ();
    cout << "radius=" << radius << endl;
}
class Ccylinder : public CCircle
{
    private:
        double height;
    public:
        Ccylinder(double tx, double ty, double r, double h);
        Ccylinder (Ccylinder & cy);
        virtual double GetArea();
        virtual double GetVolum ();
        virtual void print ();
};

Ccylinder::Ccylinder(double tx, double ty, double r, double h) : CCircle(tx, ty, r), height(h){}
Ccylinder::Ccylinder (Ccylinder & cy) :CCircle(cy), height(cy.height){}

double Ccylinder ::  GetArea()
{
    double di, y;
    di = CCircle ::GetVolum ();
    y = CCircle ::GetArea();
    return 2 * y + height * di;
}

double Ccylinder :: GetVolum()
{
    double s;
    s = CCircle ::GetArea();
    return s*height;
}

void Ccylinder ::print()
{
    CCircle ::print();
    cout << "height:" << height << endl;
    cout << "BasalArea:" << CCircle :: GetArea() << endl;
    cout << "SupfaceArea:" << Ccylinder ::GetArea() << endl;
    cout << "Volume:" << GetVolum() << endl;
}

继承关系和复合关系

继承关系

继承:“是”关系。

  • 基类A,B是基类A的派生类。
  • 逻辑上要求:“一个B对象也是一个A对象”。
    在这里插入图片描述

复合关系

复合:“有”关系。

  • 类C中“有”成员变量k,k是类D的对象,则C和D是复合关系
  • 一般逻辑上要求:“D对象是C对象的固有属性或组成部分”。
    在这里插入图片描述

派生类覆盖基类成员

覆盖

派生类可以定义一个和基类成员同名的成员,这叫覆盖。

  • 在派生类中访问这类成员时,缺省的情况是访问派生类中定义的成员。
  • 要在派生类中访问由基类定义的同名成员时,要使用作用域符号::。
class base 
{
	int j;
	public:
		int i;
		void func();
};
class derived :public base
{
	public:
		int i;
		void access();
		void func();
};

void derived::access() 
{
	j = 5; //error
	i = 5; //引用的是派生类的 i
	base::i = 5; //引用的是基类的 i
	func(); //派生类的
	base::func(); //基类的
}

derived obj;
obj.i = 1;  //引用的是派生类的i
obj.base::i = 1; //引用的是基类的i

类的保护成员

权限说明符:protected

类的成员可被访问范围

1、基类的private成员:可以被下列函数访问

  • 基类的成员函数
  • 基类的友元函数

2、基类的public成员:可以被下列函数访问

  • 基类的成员函数
  • 基类的友元函数
  • 派生类的成员函数
  • 派生类的友元函数
  • 其他的函数

3、基类的protected成员:可以被下列函数访问

  • 基类的成员函数
  • 基类的友元函数
  • 派生类的成员函数可以访问当前对象的基类的保护成员

例:

class Father 
{
	private: int nPrivate; // 私有成员
	public: int nPublic; // 公有成员
	protected: int nProtected; //  保护成员
};
class Son :public Father
{
	void AccessFather () 
	{
		nPublic = 1; // ok;
		nPrivate = 1; // wrong
		nProtected = 1; // OK ,访问从基类继承的protected 成员
		Son f;
		f.nProtected = 1; //wrong  ,f 不是当前对象
	}
};

派生类的构造函数

1、在创建派生类的对象时,需要调用基类的构造函数:初始化派生类对象中从基类继承的成员。
2、在执行一个派生类的构造函数之前,总是先执行基类的构造函数。

  • 调用基类构造函数的两种方式
    – 显式方式:在派生类的构造函数中,为基类的构造函数提供
    参数.
CCircle::CCircle(double tx, double ty, double r) :CPoint(tx, ty), radius(r) {}
//采用初始化列表的方式

– 隐式方式:在派生类的构造函数中,省略基类构造函数时,派生类的构造函数则自动调用基类的默认构造函数.

CCircle :: CCircle(double r) : radius(r) {} 
//派生类的构造函数则自动调用基类的默认构造函数

3、派生类的析构函数被执行时,执行完派生类的析构函数后,自
动调用基类的析构函数。

class CPoint
{
    private:
        double x, y;
    public:                           
        CPoint(double tx, double ty);
        CPoint (CPoint & p);
} ;
CPoint :: CPoint(double tx, double ty) : x(tx), y(ty) {} 
CPoint :: CPoint(CPoint & p) : x(p.x), y(p.y) {}
class CCircle : public CPoint
{
    private:
        double radius;
    public:
        CCircle(double tx, double ty, double r);
        CCircle(CCircle & c);
};

CCircle :: CCircle(double tx, double ty, double r) :CPoint(tx, ty), radius(r) {}
CCircle::CCircle(CCircle & c) : CPoint :: CPoint(c), radius (c.radius){}

包含成员对象的派生类的构造函数写法

class Bug 
{
	private :
		int nLegs; int nColor;
	public:
		int nType;
		Bug ( int legs, int color);
		void PrintBug (){ };
};
class Skill 
{
	public:
		Skill(int n) { }
};
class FlyBug: public Bug 
{
	int nWings;
	Skill sk1, sk2;
	public:
		FlyBug( int legs, int color, int wings);
};
FlyBug::FlyBug( int legs, int color, int wings):
Bug(legs,color),sk1(5),sk2(color) ,nWings(wings) {}
//采用初始化列表方式,调用成员对象构造函数

封闭派生类对象的构造函数和析构函数执行顺序

1、在创建派生类的对象时:

  • 先执行基类的构造函数,用以初始化派生类对象中从基类继承的成员;
  • 再执行成员对象类的构造函数,用以初始化派生类对象中成员对象。
  • 最后执行派生类自己的构造函数

2、在派生类对象消亡时:

  • 先执行派生类自己的析构函数
  • 再依次执行各成员对象类的析构函数
  • 最后执行基类的析构函数

析构函数的调用顺序与构造函数的调用顺序相反。

public继承的赋值兼容规则

1 派生类的对象可以赋值给基类对象
	b = d;
2 派生类对象可以初始化基类引用
	base & br = d;
3 派生类对象的地址可以赋值给基类指针
	base * pb = & d;

 如果派生方式是 private或protected,则上述三条不可行。

protected继承和private继承

  • protected继承时,基类的public成员和protected成员成为派生类protected成员。
  • private继承时,基类的public成员成为派生类的private成员,基类protected成员成为派生类的不可访问成员。
  • protected和private继承不是“是”的关系。

基类与派生类的指针强制转换

1、公有派生的情况下,派生类对象的指针可以直接赋值给基类指针

Base * ptrBase = &objDerived;
/*
ptrBase指向的是一个Derived类的对象;
*ptrBase可以看作一个Base类的对象,访问它的public成员直接通过ptrBase即可
但不能通过ptrBase访问objDerived对象中属于Derived类而不属Base类的成员
*/

2、即便基类指针指向的是一个派生类的对象,也不能通过基类指针访问基类没有,而派生类中有的成员。

3、通过强制指针类型转换,可以把ptrBase转换成Derived类的指针

Base * ptrBase = &objDerived;
Derived *ptrDerived = (Derived * ) ptrBase;

//要保证ptrBase指向的是一个Derived类的对象,否则很容易会出错。
class Base 
{
	protected:
		int n;
	public:
		Base(int i):n(i)
		{
			cout << "Base " << n << " constructed" << endl; 
		}
		~Base() 
		{
			cout << "Base " << n <<" destructed" << endl;
		}
		void Print() 
		{ 
			cout << "Base:n=" << n << endl;
		}
};
class Derived:public Base 
{
	public:
		int v;
		Derived(int i):Base(i),v(2 * i) 
		{
			cout << "Derived constructed" << endl;
		}
		~Derived()  
		{
			cout << "Derived destructed" << endl;
		}
		void Func() { } ;
		void Print() 
		{
			cout << "Derived:v=" << v << endl;
			cout << "Derived:n=" << n << endl;
		}
};
int main() 
{
	Base objBase(5);
	Derived objDerived(3);
	Base * pBase = & objDerived ;
	pBase->Func();   //err;Base 类没有Func() 成员函数
	pBase->v = 5;    //err; Base 类没有v 成员变量
	pBase->Print();
	Derived * pDerived = & objBase; //error  派生类指针不能直接指向基类,需进行强制类型转换
	Derived * pDerived = (Derived *)(& objBase);
	pDerived->Print(); // 慎用,可能出现不可预期的错误
	pDerived->v = 128; // 往别人的空间里写入数据,会有问题
	objDerived.Print();
	return 0;
}

直接基类与间接基类

类A派生类B,类B派生类C,类C派生类D,……

  • 类A是类B的直接基类
  • 类B是类C的直接基类,类A是类C的间接基类
  • 类C是类D的直接基类,类A、B是类D的间接基类

在声明派生类时,只需要列出它的直接基类

  • 派生类沿着类的层次自动向上继承它的间接基类
  • 派生类的成员包括:
    —— 派生类自己定义的成员
    ——直接基类中的所有成员
    ——所有间接基类的全部成员

1、派生类对象生成时,基类的构造函数先于派生类的构造函数执行

2、派生类对象消亡时,派生类的析构函数先于基类的析构函数执行

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值