继承是面向对象语言的一个重要机制,通过继承可以在一个一般类的基础上建立新类。被继承的类成为基类,在基类上建立的新类称为派生类,太儿戏了,哈哈。
单继承:一个类只有1个基类
多继承:一个类有2个或2个以上的基类
二、继承的格式
1.单继承
class <派生类名> : <继承方式> <基类名>
{
<派生类的成员>
};
2.多继承
class <派生类名> : <继承方式1> <基类名1>, <继承方式2> <基类名2>
{
<派生类的成员>
};
3.补充--默认继承的保护级别
a.class关键字定义的派生类默认是private继承
b.struct关键字定义的派生类默认是public继承
这与之前介绍过的class与struct的区别是一样的,在struct声明类中,默认是public访问级别,而class声明的类中,默认是private访问级别。
class的例子:
下面继承的关系等价于:class CDerived : private CBase
class CBase
{
...
};
class CDerived : CBase
{
...
};
struct的例子:
下面继承的关系等价于:struct CDerived : public CBase
struct CBase
{
...
};
struct CDerived : CBase
{
...
};
三、不同继承方式的基类特性与派生类特性
下面给一个表,是一个继承的规则,基本上必须要熟记,用几次就熟练了。
- 继承方式 基类特性 派生类特性
- 公有继承(public) public public
- protected protected
- private 不可访问
- 私有继承(private) public private
- protected private
- private 不可访问
- 保护继承(protected) public protected
- protected protected
- private 不可访问
四、公有继承的例子
在公有继承中,
1.派生类的对象可以访问基类中的公有成员;
2.派生类的成员函数可以访问基类的公有成员和保护成员。
- #include <iostream>
- using namespace std;
- class CAnimal
- {
- public:
- CAnimal(const char *name) : mName(name) {}
- string GetName() const
- {
- return mName;
- }
- protected:
- void SetName(const char *name)
- {
- mName.assign(name);
- }
- private:
- string mName;
- };
- class CDog : public CAnimal
- {
- public:
- CDog(const char *name) : CAnimal(name) {}
- string GetDogName() const
- {
- return GetName();
- }
- void SetDogName(const char *name)
- {
- SetName(name);
- }
- };
- int main()
- {
- CDog dog("Tom");
- cout << "小狗的名字是:" << dog.GetDogName() << endl;
- cout << "动物的名字是:" << dog.CAnimal::GetName() << endl;
- cout << endl;
- dog.SetDogName("Wali");
- cout << "小狗的新名字是:" << dog.GetDogName() << endl;
- cout << "动物的新名字是:" << dog.CAnimal::GetName() << endl;
- cout << endl;
- cout << "小狗的名字是:" << dog.GetName() << endl; //正确, 派生类的对象可以调用基类的公有成员
- // cout << dog.SetName("Cat"); //错误, 派生类的对象不能调用基类的保护成员
- return 0;
- }
执行结果:
小狗的名字是:Tom
动物的名字是:Tom
小狗的新名字是:Wali
动物的新名字是:Wali
小狗的名字是:Wali
五、私有继承的例子
在私有继承中,
1.派生类的对象不可以访问基类中的所有成员;
2.派生类的成员函数可以访问基类的公有成员和保护成员。
以下的例子跟公有继承的例子差不多,只是将几行代码给注释掉了,被注释掉的都是因为private方式的继承不能调用成功,都是错误的。
- #include <iostream>
- using namespace std;
- class CAnimal
- {
- public:
- CAnimal(const char *name) : mName(name) {}
- string GetName() const
- {
- return mName;
- }
- protected:
- void SetName(const char *name)
- {
- mName.assign(name);
- }
- private:
- string mName;
- };
- class CDog : private CAnimal
- {
- public:
- CDog(const char *name) : CAnimal(name) {}
- string GetDogName() const
- {
- return GetName();
- }
- void SetDogName(const char *name)
- {
- SetName(name);
- }
- };
- int main()
- {
- CDog dog("Tom");
- cout << "小狗的名字是:" << dog.GetDogName() << endl;
- // cout << "动物的名字是:" << dog.CAnimal::GetName() << endl;
- cout << endl;
- dog.SetDogName("Wali");
- cout << "小狗的新名字是:" << dog.GetDogName() << endl;
- // cout << "动物的新名字是:" << dog.CAnimal::GetName() << endl;
- cout << endl;
- // cout << "小狗的名字是:" << dog.GetName() << endl;
- // cout << dog.SetName("Cat");
- return 0;
- }
执行结果:
小狗的名字是:Tom
小狗的新名字是:Wali
六、保护继承的例子
保护继承的例子跟私有继承的例子是一样的,规则差不多。
在保护继承中,
1.派生类的对象不可以访问基类中的所有成员;
2.派生类的成员函数可以访问基类的公有成员和保护成员。
七、对上述例子的总结
派生类对象和派生类中的成员函数对基类的访问不同。
在公有继承中,
1.派生类的对象可以访问基类中的公有成员
2.派生类的成员函数可以访问基类的公有成员和保护成员。
在私有继承和保护继承中,
1.派生类的对象不可以访问基类中的所有成员
2.派生类的成员函数可以访问基类的公有成员和保护成员。
八、友元关系与继承
友元关系不能被继承。基类的友元对派生类的成员没有特殊访问权限,例如在CBase中声明了一个友元类CFriend,由CBase类派生出CDerived类,在CFriend类中,可以访问基类CBase中的所有成员和派生类CDerived中属于CBase部分的成员,看例子:
- #include <iostream>
- using namespace std;
- class CBase
- {
- public:
- friend class CFriend;
- private:
- int mBase;
- };
- class CDerived : public CBase
- {
- private:
- int mDerived;
- };
- class CFriend
- {
- public:
- int GetVal(CBase b) const
- {
- return b.mBase;
- }
- int GetVal1(CDerived d) const
- {
- return d.mBase;
- }
- // int GetVal2(CDerived d) const
- // {
- // return d.mDerived;
- // }
- };
- int main()
- {
- return 0;
- }
被注释掉的是错误的,不能访问派生类中自己的部分成员,而可以访问派生类中属于基类的部分成员。
九、静态成员与继承
之前说过,在一个类的所有对象中,静态成员只保存了一个副本,不属于对象的成员,所有对象共享同一个static成员。
在继承过程中,原理也是一样。若基类定义了static成员,则在整个继承层次中只有一个静态成员。所以无论从基类派生出多少个派生类,声明了多少对象,每个static成员只有一个实例。
看个例子:
- #include <iostream>
- using namespace std;
- class CBase
- {
- public:
- void Print() const
- {
- cout << "CBase: mStatic = " << mStatic << endl;
- }
- static int mStatic;
- };
- int CBase::mStatic = 2;
- class CDerived : public CBase
- {
- public:
- void Print() const
- {
- cout << "CDerived: mStatic = " << mStatic << endl;
- }
- };
- int main()
- {
- CBase base;
- CDerived derived;
- base.Print();
- derived.Print();
- cout << endl;
- base.mStatic = 4;
- base.Print();
- derived.Print();
- return 0;
- }
执行结果:
CBase: mStatic = 2
CDerived: mStatic = 2
CBase: mStatic = 4
CDerived: mStatic = 4
十、继承过程中,构造函数与析构函数的调用
派生类与基类的构造函数和析构函数的调用顺序,写个小例子便可知分晓:
- #include <iostream>
- using namespace std;
- class CBase
- {
- public:
- CBase()
- {
- cout << "调用基类的构造函数" << endl;
- }
- ~CBase()
- {
- cout << "调用基类的析构函数" << endl;
- }
- };
- class CDerived : public CBase
- {
- public:
- CDerived()
- {
- cout << "调用派生类的构造函数" << endl;
- }
- ~CDerived()
- {
- cout << "调用派生类的析构函数" << endl;
- }
- };
- int main()
- {
- CDerived obj;
- return 0;
- }
执行结果:
调用基类的构造函数
调用派生类的构造函数
调用派生类的析构函数
调用基类的析构函数
顺序是:基类的构造函数 -> 派生类的构造函数 -> 派生类的析构函数 -> 基类的析构函数