更多参考其他文档菜鸟文档W3C微软C++文档

继承和派生

继承和派生的概念

  • 继承:在定义以恶搞新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个派生类(也称子类)。
  • 派生类是通过对基类进行修改和扩充得到的。在派生类中,可以扩充新的成员变量和成员函数。
  • 派生类一经定义后,可以独立适用,不依赖于基类。
  • 派生类拥有基类的全部成员函数和成员变量,不论是private、protected、public。
    • 在派生类的各个成员函数中,不能访问基类中的private成员。

需要继承的例子

派生类的写法


//语法
class 派生类名 :public 基类名
{

};


//实例
class CStudent
{
private:
string sName;
int nAge;
public:
bool IsThreeGood(){};
void SetName(const string & name){sName = name;}
}

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(){};
};

派生类对象的内存空间

  • 派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。在派生类对象中,包含着基类对象,而且基类对象的存储位置位于类派生类对象新增的成员变量之前。
class CBase
{
int v1,v2;
};
class CDerived:public CBase
{
int v3;
};
  • 派生类对象包含基类对象,派生类对象新增的内容位于基类对象之后
//实例
#include <iostream>
#include <string>
using namespace std;

class CStudent
{
private:
string name;
string id;
char gender;
int age;

public:
void Printlnfo();
void Setlnfo(const string &name_, const string &id_, int age_, char gender_);
string GetName() { return name; }
};

class CUndergraduateStudent : public CStudent
{
private:
string department;

public:
void QualifiedForBaoyan()
{ //给予保研资格
cout << "qualified for baoyan" << endl;
}
void printlnfo()
{
CStudent::Printlnfo(); //调用基类的Printlnfo
cout << "Department:" << department << endl;
}
void Setlnfo(const string &name_, const string &id_, int age_, char gender_, const string &department_)
{
CStudent::Setlnfo(name_, id_, age_, gender_); //调用基类的Setinfo
department = department_;
}
};

int main()
{
CUndergraduateStudent s2;
s2.Setlnfo("Harray Potter", "118829212", 19, 'M', "Computer Science");
cout << s2.GetName() << "";
s2.QualifiedForBaoyan();
s2.printlnfo();
return 0;
}

继承关系和复合关系

  • 继承 “是”关系

    • 满足逻辑要求“ 一个B对象也是一个C对象”
    • 基类A,B是基类A的派生类
  • 复合 “有”关系

    • 类C中“有”成员变量k,k是类D的对象,则C和D是复合关系
    • 一般逻辑上要求:“D对象是C对象的固有属性或组成部分”
  • 继承关系使用

    • 人 对男人和女人 的关系,把人作为基类,男人和女人作为人的派生类
  • 复合关系使用

    • 需要“点”类,也需要“圆”类时,两者的关系就是复合关系,即每一个圆包含一个点

      //正确做法
      class CPoint
      {
      double x,y;
      friend class CCircle;
      //便于Ccirle类操作其圆心
      };

      class CCircle
      {
      double r;
      CPoint center;
      };
    • 小区业主与狗的关系

      //这样的定义是错的,循环定义了
      class CDog;
      class CMaster
      {
      CDog dogs[10];
      };

      class CDog
      {
      CMaster m;
      };


      //改进

      class CDog;
      class CMaster
      {
      CDog * dogs[10]; //CDog类的指针数组
      };
      class CDog{
      CMaster m;
      };
      //依然不行 在调用修改狗的主人的信息时,需要维持不同狗之间的一致性,比较繁琐

      //再次改进

      class CMaster; //先声明
      class CDog{
      CMaster *pm;
      };
      class CMaster{
      //这样使得狗对象只能通过人来调用,失去了自由
      CDog dogs[10];
      };

      //正确做法
      class CMsater;

      class CDog{
      CMaster *pm;
      };
      class CMaster{
      CDog *dogs[10]; //这种关系也被称为"知道"
      };

派生类覆盖基类成员

  • 派生类可以定义一个和基类成员同名的成员,这叫做覆盖。== 在派生类中访问这类成员时,缺省的情况是访问派生类中定义的成员。要在派生类中访问由类定义的同名成员时,要使用作用域符号 ::

    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();//派生类fun
    base::func();//基类fun
    }

    //注意
    一般来说,基类和派生类不定义同名的成员变量
    //占用空间
    | base::j |
    | base::i |
    | i |

类的保护成员

private:   //基类的成员函数、友元函数可以访问
public//基本上都可以访问
protected : //基类的成员函数、友元函数、派生类的成员函数可以访问当前对象的基类的保护成员


class Father{
private: int nPrivate;
public: int nPublic;
protected: int nProtected;
};
class Son: public Father{
void AccessFatther()
{
nPublic = 1;// ok
nPrivate = 1 ; //wrong 访问了基类的私有成员
nProtected = 1; // ok ,派生类可以访问基类的保护成员
Son f;
f.nProtected = 1; //wrong ,f不是当前对象
}
};

int main()
{
Father f;
Son s;
f.nPublic = 1;// OK
s.nPublic = 1; //OK
f.nProtected = 1; //error 保护
f.nPrivate = 1; //error 私有
s.nProtected = 1;//error 保护
s.nPrivate = 1; //error 私有
return 0;
}

派生类的构造函数

#include <iostream>
using namespace std;

class Bug //基类
{
private:
int nLegs;
int nColor;

public:
int nType; //公有
Bug(int legs, int color);
void PrintBug(){};
};

class FlyBug : public Bug //派生类
{
int nWings;
public:
FlyBug(int legs, int color, int wings);
};

Bug::Bug(int legs, int color)
{
nLegs = legs;
nColor = color;
}


//错误 的派生类构造函数
FlyBug::FlyBug(int legs, int color, int wings)//派生类成员函数
{
nLegs = legs; //访问的是基类的私有成员数据 错误
nColor = color; //访问的是基类的私有成员数据 错误
nType = 1; //访问基类的公有成员
nWings = wings; //自己的私有成员
}
//正确 的派生类构造函数
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;
}

int main()
{
FlyBug fb(2, 3, 4);
fb.nType = 1;
fb.nLegs = 2; //这里同样不能调用基类的私有成员 错误
return 0;
}
//在一个派生类的构造函数被执行之前,总是先执行基类的构造函数
//相反,在派生类的析构函数被执行时,执行完派生类的析构函数后,自动调用基类的构造函数
  • 调用基类构造函数的两种方式

    • 显示方式:在派生类的构造函数中,为基类的构造函数提供参数

      derived::derived(arg_derived-list):base(arg_base-list)
    • 隐式方式:在派生类的构造函数中,省略基类构造函数时,派生类的构造函数则自动调用基类的默认构造函数。

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

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){}

生成时:

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

消亡时:

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

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

public 继承的赋值兼容规则

class base{};
class derived:public base{};
base b;
derived d;
  • 派生类对象可以赋值给基类对象

    b = d;
  • 派生类对象可以初始化基类引用

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

    base * pb = & d;

直接基类与间接基类

A-派生-B-派生-C

A是B的直接基类,A是C的间接基类

  • 声明派生类时,只需要列出它的直接基类
    • 派生类沿着类的层次自动向上继承它的间接基类
  • 派生类的成员包括
    • 派生类自己定义的成员
    • 直接基类中的所有成员
    • 所有间接基类的全部成员
#include<iostream>

using namespace std;

class Base{
public:
int n;
Base(int i):n(i)
{
cout << "Base" << n << "constructed" << endl;
}
~Base(){
cout << "Base" << n << "dextructed" << endl;
}
};
class Derived:public Base
{
public:
Derived(int i):Base(i){
cout << "Derived costructed" << endl;
}
~Derived()
{
cout << "Derived dsetucted" << endl;
}
};

class MoreDerived:public Derived
{
public:
MoreDerived():Derived(4) //只用写明直接基类的成员赋值就行,不需要写间接基类
{
cout << "More Derived constructed" << endl;
}
~MoreDerived()
{
cout << "More Derived destructed" << endl;
}
};

int main()
{
MoreDerived Obj; //从最顶层基类开始构造
return 0;
} //先析构最后派生的类