继承与派生

定义

继承

  • 在定义一个新的类B时,如果该类与某个已有的类A相似**(指的是B拥有A的全部特点)**
  • 那么就可以把A作为一个基类
  • 把B作为基类的派生类(也称子类)
  • 派生类是通过对基类进行修改和扩充得到的。

  • 在派生类中,可以扩充新的成员变量和成员函数

  • 派生类一经定义,可以独立使用,不依赖于基类

  • 派生类拥有基类的全部成员函数和成员变量

    • 但是派生类各个成员函数,依然不能访问基类的private成员

继承例子

所有学生都有的共有属性:

  • 姓名
  • 学号
  • 性别
  • 成绩

共有的成员函数:

  • 入学
  • 退学
  • 是否留级
  • 是否保研等等

不同学生又有格子不同的属性和方法:

  • 研究生:导师、系
  • 大学生:系
  • 中学生:竞赛加分
//形式:
class 派生类名:public 基类名
{
    
};
#include <iostream> 
#include <cstring>  
using namespace std;
class CStudent
{
	private:
		string sName;
		int nAge;
	public:
		bool IsThreeGood(){ };//是不是三好学生
		void SetName(const string& name)
		{
			sName=name;
		}
};
//未毕业学生派生类,从CStudent派生而来
class CUndergraduateStudent:public CStudent
{
	private:
		int nDepartment;
		//扩充派生类独有的新的成员函数
	public:
		bool IsThreeGood(){ };//覆盖
		//有不同的评判三好学生的标准,可以用同名函数进行覆盖
		bool CanBaoYan(){};
};
//已毕业学生派生类
class CGraduatedStudent:public CStudent
{
	private:
		int nDepartment;
		char szMentorName[20];
		//系和导师
	public:
		int CountSalary(){ };
};

派生类对象的内存空间

派生类对象的体积,等于基类对象的体积+自己扩充的新成员变量的体积

在派生类对象中,包含着基类对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前

#include <iostream>
#include <cstring>
using namespace std;
class CStudent
{
	private:
		string name;
		string id;
		char gender;//性别,F为女生,M为男生
		int age;
	public:
		void PrintInfo()
		{
			cout<<name<<" "<<id<<endl;
			cout<<gender<<" "<<age<<endl;
		}
		void SetInfo(const string& name_,const string& id_,int age_,char gender_)
		{
			name=name_;
			id=id_;
			age=age_;
			gender=gender_;
		}
		string GetName()
		{return name;}
};

class CUndergraduateStudent:public CStudent
{//本科生类,继承了CStudent
	private:
		string department;//学生所属的系的名称
	public:
		void QualifiedForBaoYan()
		{//给予保研资格
			cout<<"qualified for BaoYan"<<endl;
		}
		void PrintInfo()
		{
			CStudent::PrintInfo();//调用基类的PrintInfo
			//先完成基类的事
			cout<<"Department:"<<department<<endl;
			//再完成派生类特有的事
		}//对基类的修改,表达出派生类对基类的修改
		void SetInfo(const string& name_,const string& id_,int age_,char gender_,const string& depaetment_)
		{
			CStudent::SetInfo(name_,id_,age_,gender_);//调用基类的SetInfo
			department=depaetment_;
		}//对基类的修改,表达出派生类对基类的修改
};

int main()
{
	CUndergraduateStudent s2;
	s2.SetInfo("Harry Potter","118829212",19,'M',"Computer Science");
	cout<<s2.GetName()<<endl;
	s2.QualifiedForBaoYan();
	s2.PrintInfo();
	return 0;
}

继承关系和复合关系

利用面向对象程序设计,一定要处理好类与类之间的关系

类与类主要有三种关系:

  • 没有关系
  • 继承关系
  • 复合关系

继承关系

  • “是”的关系
  • B是基类A的派生类
  • 逻辑上要求一个B对象也是一个A对象
    • 也具有A的成员变量和成员函数

继承关系的使用eg:

我们有一个CMan类,现在要写一个CWomen类

!!!不能直接从CMan类中派生出CWomen

应先写CHuman类,再派生出两个类

复合关系

  • “有”的关系
  • 类C中“有”成员变量K,k是类D的对象,则C和D是复合关系
  • 逻辑上要求D对象是C对象的固有属性或组成部分
  • ???封闭类

复合关系的使用eg:

有一个点类,有一个圆类

逻辑上,圆不是一个点,因此不符合继承关系

圆是无数多个点形成,有同样的xy,所以应于点是复合关系

class CPoint
{
  double x,y;
  friend class CCircle;
    //类的友元化
};
class CCircle
{
    double r;
    CPoint center;
};

复合关系易错

容易循环定义

//小区里养狗管理系统,小区里每个业主最多有十条狗,每条狗只有一个主任
//错误写法
class CDog;
class CMaster
{
    CDog dogs[10];
};
class CDog
{
    CMaster m;
};

//另一种错误写法:
1.为 狗类 设一个 业主类的成员对象
2.为 业主类 设一个 狗类的对象指针数组
    
class CDog;
class CMaster
{
    CDog* dogs[10];
};
class CDog
{
    CMaster m;
};

//凑合的写法:
1.为 狗类 设一个 业主类的对象指针
2.为 业主类 设一个 狗类的对象数组
class CMaster;
class CDog
{
    CMaster* pm;
};
class CMaster
{
    CDog dogs[10];
};

//正确的写法
为 狗类 设一个 业主类的对象指针
为 业主类 设一个 狗类的对象指针数组
class CMaster;
class CDog
{
    CMasetr* pm;
};
class CMaster
{
    CDog* dogs[10];
};

覆盖与保护成员

覆盖

派生类覆盖基类成员

  • 派生类可以定义一个和基类成员同名的成员,这就叫覆盖
  • 在派生类访问这类成员,缺省的情况是访问派生类中新定义的成员
  • 若要在派生类中,访问基类定义的同名成员时,要使用作用域符号::
#include <iostream>
#include <cstring>
using namespace std;
class base
{
		int j;
	public:
		int i;
		void func();
};
class derived:public base
{
	public:
		int i;
		//基类与派生类,同名的函数常见,同名的成员变量是傻子
		void access()
		{
			//j=5;//error
			//base::j=5;//error基类的j是private
			i=5;//引用的是派生类的i
			base::i=5;//引用的是基类的i
			func();//派生类的函数
			base::func();//基类的函数
		}
		void func();
};
int main()
{
	derived obj;
	obj.i=1;//派生类的i
	obj.base::i=1;
}

保护成员

protected

!!!派生类不能访问基类的私有成员

因此有共同的成员变量时,把共同部分设置为保护成员,而不是public成员

#include <iostream>
#include <cstring>
using namespace std;
class Human
{
	private:
		int nprivate;
	public:
		int npublic;
	protected:
		int nprotected;
};
class Man:public Human
{
	void AccessFather()
	{
		npublic=1;
		nprivate=1;//error
		nprotected=1;//ok,派生类可以访问当前对象基类的protected对象
		Man f;
		f.nprotected=1;//wrong,f不是当前对象
	}
};

派生类的构造函数

#include <iostream>
#include <cstring>
using namespace std;
class Bug
{
	private:
		int nLegs,nColor;
	public:
		int nType;
		Bug(int nL,int nC):nLegs(nL),nColor(nC){ }
		void PrintBug()
		{
			cout<<nLegs<<" "<<nColor<<endl;
		}
};
class FlyBug:public Bug
{
		int nWings;
	public:
		FlyBug(int legs,int color,int wings);
		//问题1,派生类不能访问基类的私有成员
};

//错误写法!!!
FlyBug::FlyBug(int legs,int color,int wings)
{
	nLegs=legs;//error
	nColor=color;//error
	nWings=wings;//ok
	nType=1;//ok
}
//正确做法:用初始化列表对Bug私有成员进行初始化,调用基类的构造函数
FlyBug::FlyBug(int legs,int color,int wings):Bug(legs,color)
{
	nWings=wings;
}

在创建派生类的对象时,需要调用基类的构造函数

先执行基类的构造函数,再执行一个派生类的构造函数

先执行派生类的析构函数,再调用基类的析构函数

调用基类构造函数的两种方式

  • 显性调用

    • 在派生类的构造函数中,为基类的构造函数提供参数
    FlyBug::FlyBug(int legs,int color,int wings):Bug(legs,color)
    {
    	nWings=wings;
    }
    
  • 隐式方式:

    • 在派生类的构造函数中,省略基类构造函数时,派生类的构造函数则自动调用基类的默认构造函数(即一个无参构造函数)

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

说白了,就是要有一个初始化列表,把该初始化的都初始化

  • 在创建派生类的对象时:
    1. 先执行基类的构造函数,用来初始化派生类对象中从基类继承的成员
    2. 再执行成员对象类的构造函数,用以初始化派生类对象中成员对象。
#include <iostream>
#include <cstring>
using namespace std;
class Bug
{
	private:
		int nLegs,nColor;
	public:
		int nType;
		Bug(int nL,int nC):nLegs(nL),nColor(nC){ }
		void PrintBug()
		{
			cout<<nLegs<<" "<<nColor<<endl;
		}
};
class Skill
{
	public:
		Skill(int n){ }
};
class FlyBug:public Bug
{
		int nWings;
		Skill sk1,sk2;
		//每个封闭类对象都要参与初始化
	public:
		FlyBug(int legs,int color,int wings):Bug(legs,color),sk1(5),sk2(color),nWings(wings){ }
};

公有继承的赋值兼容规则

public继承就是

class derived : public base{ }

  • 规则一:派生类的对象可以赋值给基类对象

    base b;//基类
    derived d;//派生类
    b=d;//基类=派生类
    未重载,缺省情况下,基类包含的部分拷贝
    
  • 规则二:派生类对象可以初始化基类引用

    base& br=d;
    
  • 规则三:派生类对象的地址可以赋值给基类指针

    base* pb=&d;
    //基类指针指向派生类对象
    
  • 如果派生方式是private或protected,则上述三条不可行

直接基类和间接基类

套娃继承

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

  • 派生类的成员包括:
    • 派生类自己定义的成员
    • 直接基类中的所有成员
    • 所有间接基类的全部成员
#include <iostream>
#include <cstring>
using namespace std;
class Base
{
	public:
		int n;
		Base(int i):n(i)
		{
			cout<<"Base "<<n<<" constructed"<<endl;
		}
		~Base()
		{
			cout<<"Base "<<n<<" destructed"<<endl;
		}
};

class Derived:public Base
{
	public:
		Derived(int i):Base(i)
		{
			cout<<"Derived constructed"<<endl;
		}
		~Derived()
		{
			cout<<"Derived destructed"<<endl;
		}
};

class MoreDerived:public Derived
{
	public:
		MoreDerived(int i):Derived(i)
		{
			cout<<"MoreDerived constructed"<<endl;
		}
		~MoreDerived()
		{
			cout<<"MoreDerived destructed"<<endl;
		}
};

int main()
{
	MoreDerived obj(1);
	return 0;
}

!!!最基类的先构造,最派生的先析构

Base 1 constructed
Derived constructed
MoreDerived constructed
MoreDerived destructed
Derived destructed
Base 1 destructed

全面的MyString

class MyString
{
	public:
		char* p;
		MyString(const char* s=NULL)
		{
			if(s)
			{
				p=new char[strlen(s)+1];
				strcpy(p,s);
			}
			else
			{
				p=new char[1];
				p[0]='\0';
			}
		}//初始化值为“”
		
		MyString (const MyString& s)
		{
			if(s.p)
			{
				p=new char[strlen(s.p)+1];
				strcpy(p,s.p);
			}
			else
			{
				p=new char[1];
				p[0]='\0';
			}
		}

		~MyString()
		{
			if(p)
				delete[] p;
		}

		friend ostream& operator<<(ostream& os,const MyString& c)
		{
			os<<c.p;
			return os;
		}

		MyString& operator=(const char* s)
		{
			if(p==s)
				return *this;
			//避免自己赋值自己
			if(p)
				delete[] p;
				
			if(s)
			{
				p=new char[strlen(s)+1];
				strcpy(p,s);
			}
			else
			{
				p=new char[1];
				p[0]='\0';
			}
			
			return *this;
		}

		MyString& operator=(const MyString& s)
		{
			if(p==s.p)
				return *this;
			if(p)
				delete[] p;
			if(s.p)
			{
				p=new char[strlen(s.p)+1];
				strcpy(p,s.p);
			}
			else
			{
				p=new char[1];
				p[0]='\0';
			}
			return *this;
		}

		MyString operator+(const MyString& s)
		{
			char* tmp=new char[strlen(s.p)+strlen(p)+1];
			strcpy(tmp,p);
			strcat(tmp,s.p);
			return MyString(tmp);
		}

		friend MyString operator+(const char* c,const MyString& s)
		{
			MyString tmp;
			tmp=MyString(c)+s;
			return tmp;
		}
		MyString& operator+(const char* c)
		{
			*this=*this+MyString(c);
			return *this;
		}

		MyString& operator+=(const char* c)
		{
			*this=*this+MyString(c);
			return *this;
		}

		friend bool operator<(const MyString& a,const MyString& b)
		{
			if(strcmp(a.p,b.p)==-1)
				return true;
			return false;
		}

		friend bool operator>(const MyString& a,const MyString& b)
		{
			if(strcmp(a.p,b.p)==1)
				return true;
			return false;
		}
		friend bool operator==(const MyString& a,const MyString& b)
		{
			if(strcmp(a.p,b.p)==0)
				return true;
			return false;
		}

		char& operator[] (int i)
		{
			return p[i];
		}

		char* operator()(int start,int len)
		{
			char* tmp=new char[len+1];
			for(int i=start;i<start+len;++i)
			{
				tmp[i-start]=p[i];
			}
			tmp[len]='\0';
			return tmp;
		}
};

继承string的MyString

class MyString:public string
{
	public:
		//构造函数
		MyString(const string& p):string(p){ }
		MyString(const char* str):string(str){ }
		MyString():string(){ }//无参构造函数
		//复制构造函数
		MyString(const MyString& p):string(p){ }
		//重载()
		string operator()(int x,int y)
		{
			return string::substr(x,y);
		}
		
};

注意题

#include <iostream>
using namespace std;
class B { 
	private: 
		int nBVal; 
	public: 
		void Print() 
		{ cout << "nBVal="<< nBVal << endl; } 
		void Fun() 
		{cout << "B::Fun" << endl; } 
		B ( int n ) { nBVal = n;} 
};

class D:public B
{
    private:
        int nDVal;
    public:
        D(int n):B(3*n),nDVal(n){}
        //派生类对象,也是一个基类对象
        //派生类对象的构造函数,应当在 初始化列表处调用基类的构造函数
        void Fun()
        {cout<<"D::Fun"<<endl;}
        void Print()
        {
            B::Print();
            //就会执行当下对象的基类部分的printf
            cout<<"nDVal="<<nDVal<<endl;
        }


};
int main() { 
	B * pb; D * pd; 
	D d(4); d.Fun(); 
	pb = new B(2); pd = new D(8); 
	pb -> Fun(); pd->Fun(); 
	pb->Print (); pd->Print (); 
	pb = & d; pb->Fun(); 
    //!!!这里不是多态,这里基类的指针只能指向派生类中和基类相同的部分
    // 因为调用的两个函数都不是虚函数
	pb->Print(); 
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Caaaaaan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值