定义
继承:
- 在定义一个新的类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; }
-
隐式方式:
- 在派生类的构造函数中,省略基类构造函数时,派生类的构造函数则自动调用基类的默认构造函数(即一个无参构造函数)
包含成员对象的派生类的构造函数写法
说白了,就是要有一个初始化列表,把该初始化的都初始化
- 在创建派生类的对象时:
- 先执行基类的构造函数,用来初始化派生类对象中从基类继承的成员
- 再执行成员对象类的构造函数,用以初始化派生类对象中成员对象。
#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;
}