继承的基本概念
在之前的学习中,我们了解了类中的属性和行为的关系,这次我们谈一谈类与类之间的关系。
在对于一些类中,其实有一些共同的特性的:如下图,动物类分为猫类,狗类,猫类又可以分为好多种品种的猫,狗又可以分为很多品种的狗,但归结起来他们都是动物类,这说明类与类之间存在着一种继承的关系,下面的小类,继承了上面的大类的特性。着说明小类有自己的特性的同时,还拥有上面大类的共性。
在C++中提供了一种继承的技术,让我们能减少重复代码的书写。
继承的好处
如何减少重复代码量呢?举个猫类的例子:
假如我们设计一个
黄猫类,这个猫,会叫,会走,会吃;特别与其他猫,它是黄色的;
白猫类:这个猫,会叫,会走,会吃;特别与其他猫,它是白色的;
黑猫类:这个猫,会叫,会走,会吃;特别与其他猫,它是黑色的;
用代码实现的是:(没用继承的方式)
class YellowCat //黄猫类
{
public:
void Speak()
{
cout << "Speak()的调用" << endl;
}
void Go()
{
cout << "Go()的调用" << endl;
}
void Eat()
{
cout << "Eat()的调用" << endl;
}
void Colour()
{
cout << "我是黄色的猫" << endl;
}
};
class WhiteCat //白猫类
{
public:
void Speak()
{
cout << "Speak()的调用" << endl;
}
void Go()
{
cout << "Go()的调用" << endl;
}
void Eat()
{
cout << "Eat()的调用" << endl;
}
void Colour()
{
cout << "我是白色的猫" << endl;
}
};
class BlackCat //黑猫类
{
public:
void Speak()
{
cout << "Speak()的调用" << endl;
}
void Go()
{
cout << "Go()的调用" << endl;
}
void Eat()
{
cout << "Eat()的调用" << endl;
}
void Colour()
{
cout << "我是黑色的的猫" << endl;
}
};
void test01()
{ //创建黄猫对象,调用里面的函数
YellowCat yellowCat;
cout << "下面的是黄色的猫:" << endl<< endl;
yellowCat.Speak();
yellowCat.Go();
yellowCat.Eat();
yellowCat.Colour();
cout << endl << "-----------" << endl;
}
void test02()
{ //创建白猫对象,调用里面的函数
WhiteCat whiteCat;
cout << "下面的是白色的猫:" << endl << endl;
whiteCat.Speak();
whiteCat.Go();
whiteCat.Eat();
whiteCat.Colour();
cout << endl << "-----------" << endl;
}
void test03()
{
//创建黑猫对象,调用里面的函数
WhiteCat BlackCat;
cout << "下面的是黑猫的猫:" << endl << endl;
BlackCat.Speak();
BlackCat.Go();
BlackCat.Eat();
BlackCat.Colour();
cout << endl << "-----------" << endl;
}
int main(){
test01();
test02();
test03();
system("pause");
return 0;
}
测试结果:
重点观察黄猫和白猫和黑猫的类,里面又公共的类型:Speak,Go,Eat;就 Color不一样,所以呢这三个类都是由重复的实现部分,我们可以把重复的单独用一个类实现,然后在其他类继承。
语法:
class A : 继承方式 B
A 为子类,或派生类
B 为父类,或基类
看下面的继承方式:
class Cat //父类,基类
{
public:
void Speak()
{
cout << "Speak()的调用" << endl;
}
void Go()
{
cout << "Go()的调用" << endl;
}
void Eat()
{
cout << "Eat()的调用" << endl;
}
};
class YellowCat : public Cat //黄猫类继承猫类
{
public:
void Colour()
{
cout << "我是黄色的猫" << endl;
}
};
class WhiteCat :public Cat //白猫类继承猫类
{
public:
void Colour()
{
cout << "我是白色的猫" << endl;
}
};
class BlackCat:public Cat //黑猫类继承猫类
{
public:
void Colour()
{
cout << "我是黑色的的猫" << endl;
}
};
void test01()
{ //创建黄猫对象,调用里面的函数
YellowCat yellowCat;
cout << "下面的是黄色的猫:" << endl<< endl;
yellowCat.Speak();
yellowCat.Go();
yellowCat.Eat();
yellowCat.Colour();
cout << endl << "-----------" << endl;
}
void test02()
{ //创建白猫对象,调用里面的函数
WhiteCat whiteCat;
cout << "下面的是白色的猫:" << endl << endl;
whiteCat.Speak();
whiteCat.Go();
whiteCat.Eat();
whiteCat.Colour();
cout << endl << "-----------" << endl;
}
void test03()
{
//创建黑猫对象,调用里面的函数
WhiteCat BlackCat;
cout << "下面的是黑猫的猫:" << endl << endl;
BlackCat.Speak();
BlackCat.Go();
BlackCat.Eat();
BlackCat.Colour();
cout << endl << "-----------" << endl;
}
int main(){
test01();
test02();
test03();
system("pause");
return 0;
}
结果与普通的方式是一样的,但是在代码上却上了一大堆重复的代码。
继承方式
在c++中,继承方式和成员属性类似,都有三种权限:
共有继承:子类不可以访问父类的私有属性;在子类中继承父类的共有和保护权限不变;
保护继承:子类不可以访问父类的私有属性;在子类中继承父类的共有权限变保护权限;
私有继承:子类不可以访问父类的私有属性;在子类中继承父类的共有和保护都变成私有权限;
代码验证:共有继承方式
class Base //父类
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class Son1 : public Base //共有继承方式
{
public:
void fun()
{
m_a = 10; //父类的共有成员,在子类中可以被访问
m_b = 20; //父类的保护成员,在子类中可以被访问
m_c = 30; //错误,父类的私有成员,在子类中不可以被访问
}
};
//类外测试:子类中的对象,共有继承了父类的属性,测试,对子类对象访问的权限
void test01()
{
Son1 son1;
son1.m_a = 10; //类外可以访问父类中的共有成员属性;
son1.m_b = 20; //错误,类外不可以访问父类的保护成员属性;说明共有继承父类时候,在子类中继承父类的保护权限没有发生改变
son1.m_c = 30; //错误,类外不可以访问父类的私有成员属性;
}
代码测试:保护继承方式
class Base
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class Son1 : protected Base //共有继承方式
{
public:
void fun()
{
m_a = 10; //父类的共有成员,在子类中可以被访问
m_b = 20; //父类的保护成员,在子类中可以被访问
m_c = 30; //错误,父类的私有成员,在子类中不可以被访问
}
};
//类外测试:子类中的对象,保护继承了父类的属性,测试,对子类对象访问的权限
void test01()
{
Son1 son1;
son1.m_a = 10; //错误,保护继承父类时,在子类中,父类的共有变成了保护权限;
son1.m_b = 20; //错误,类外不可以访问父类的保护成员属性;说明保护继承父类时候,在子类中继承父类的保护权限没有发生改变
son1.m_c = 30; //错误,类外不可以访问父类的私有成员属性;
}
代码验证:私有继承方式
class Base
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class Son1 : private Base //共有继承方式
{
public:
void fun()
{
m_a = 10; //父类的共有成员,在子类中可以被访问
m_b = 20; //父类的保护成员,在子类中可以被访问
m_c = 30; //错误,父类的私有成员,在子类中不可以被访问
}
};
//类外测试:子类中的对象,保护继承了父类的属性,测试,对子类对象访问的权限
void test01()
{
Son1 son1;
son1.m_a = 10; //错误,私有继承父类时,在子类中,父类的共有变成了私有权限;
son1.m_b = 20; //错误,私有继承父类时候,在子类中继承父类的保护权限改变成为私有权限
son1.m_c = 30; //错误,类外不可以访问父类的私有成员属性;
}
继承中的对象模型大小
观察以下代码:问 Son1类继承了Base类,问 对象son的字节数是多少?
lass Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C; //私有成员只是被隐藏了,但是还是会继承下去
};
//公共继承
class Son :public Base
{
public:
int m_D;
};
void test01()
{
cout << "sizeof Son = " << sizeof(Son) << endl;
}
其结果16个字节,说明父类中的private中的属性也被继承了,只是被编译器隐藏了。
继承中的构造和析构顺序
其实构造和析构顺序之前在讲:类对象作为另一个类中的成员属性时候讲过;
都是先构造里面的,再构造外面的,析构和构造相反;
也就是说,先构造父类,再构造子类,析构和构造相反;
class Base
{
public:
Base()
{
cout << "Base构造函数!" << endl;
}
~Base()
{
cout << "Base析构函数!" << endl;
}
};
class Son : public Base
{
public:
Son()
{
cout << "Son构造函数!" << endl;
}
~Son()
{
cout << "Son析构函数!" << endl;
}
};
void test01()
{
//继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
Son s;
}
int main() {
test01();
system("pause");
return 0;
}
继承中同名成员的处理
假如继承中,子类和父类的成员同名,在创建子类对象时候,调用同名的成员时候,会输出什么呢?
假如我要通过子类对象访问父类的同名成员属性,如何访问呢?在调用时候加个父类的作用域就行。
当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数.
class Base
{
public:
int m_a;
Base()
{
m_a = 100;
cout<<"Base类的成员属性调用"<<endl;
}
void fun()
{
cout <<"Base类的成员函数的调用"<<endl;
}
};
class Son :public Base
{
public:
int m_a;
Son()
{
m_a = 200;
}
void fun()
{
cout<<"Son类的成员函数调用"<<endl;
}
};
void test01()
{
Son son;
cout << son.m_a << endl; //访问的是Son类的m_a;200
son.fun() ;//访问的是Son类的fun函数;
cout << son.Base::m_a<<endl; //访问的Base类的m_a;100
son.Base::fun();//访问的是Base类的fun函数;
}
有木有发现,虽然我调用的是 son.m_a;访问的值是200;但是也会调用父类的构造函数,并没有访问其父类Base的m_a的属性;
菱形继承的问题
子类1和子类2继承了一个父类;同时子类3,分别继承了子类1和子类2的问题;
就很典型的菱形继承问题:羊和驼分别继承了动物类;而草泥马分别继承了羊和驼类;
菱形继承会带来的问题:
会引发二义性;假如我在草泥马中通过羊,而草泥马继承下来羊和驼同时继承了动物,就会继承两份动物的数据,这是一摸一样的,没有必要。
我们可以通过虚继承的方式解决上述问题:可以解决二义性的问题;
下面是没加virtual关键字的;即不是虚继承
class Animal
{
public:
int m_Age;
};
class Sheep : public Animal {};
class Tuo : public Animal {};
class SheepTuo : public Sheep, public Tuo {};//羊驼继承了羊和驼
void test01()
{
SheepTuo st;
//羊驼访问羊里面的 m_A
st.Sheep::m_Age = 100;
//羊驼访问羊里面的 m_A
st.Tuo::m_Age = 200;
cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl; //结果 100
cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl; // 结果 200
}
int main() {
test01();
system("pause");
return 0;
}
问题来了,上面两个确实可以输出结果,可是我羊驼 到底是 100 呢,还是200 ,所以我们用虚继承方式解决问题,原理就暂时不讲,会使用先。
class Animal
{
public:
int m_Age;
};
//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {};
class Tuo : virtual public Animal {};
class SheepTuo : public Sheep, public Tuo {};
void test01()
{
SheepTuo st;
st.Sheep::m_Age = 100;
st.Tuo::m_Age = 200;
//下面结果全是200
cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;
cout << "st.m_Age = " << st.m_Age << endl;
}
int main() {
test01();
system("pause");
return 0;
}
上诉输出的都是200 ,因为用到了菱形继承的虚继承方式,把二义性的问题给解决了,这就使得羊驼的数据都是 一份,没有两份了。
总结
继承的故事就到这里了,继承就是子类继承了父类里面的成员;了解一些常用的用法;是我们的c++基础篇的目的;