🏠专栏介绍:浅尝C++专栏是用于记录C++语法基础、STL及内存剖析等。
🎯每日格言:每日努力一点点,技术变化看得见。
文章目录
继承的概念及定义
继承的概念
我们生活中也有继承的例子,例如:小明继承了孙老师傅做拉面的手艺。继承就是一种延续、复用的方式。C++为了提高代码的可复用性,引入了继承机制,概念如下↓↓↓
继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。
继承的定义
定义格式
下图演示的继承的格式,其中Person是父类,也称作基类;Student是子类,也称作派生类。
下面给出代码示例(下面代码中,Student类继承父类Person)↓↓↓
#include <iostream>
using namespace std;
class Person
{
public:
void Show()
{
cout << _name << " " << _age << endl;
}
protected:
string _name = "jammingpro"; //姓名
int _age = 18; //年龄
};
class Student : public Person
{
private:
int _stuId;
};
int main()
{
Person p;
Student s;
p.Show();
s.Show();
return 0;
}
上面代码中,Student继承父类Person的成员(成员函数+成员变量)后,这些成员都变成了子类的一部分。这里的Student复用了Person的成员。通过监视窗口可以看到,Student中也有自己的_name、_age成员变量。
继承关系与访问限定符
在C++的继承机制中,包含3种继承方式及3种类访问限定符(如下图所示),下面将分别介绍它们。
我们在学习类和对象时,就已经接触过访问限定符。其中public成员可以在类外访问,而protected与private成员不能在类外访问。但这里的protected和private在继承时是有区别的:
●如果父类愿意让自己的成员被外界访问并愿意让子类继承,则定义为public的;
●如果父类希望自己的成员不被外界访问而愿意让子类继承,则需要定义为protected;
●如果父类不希望自己的成员被外界访问、被继承,则需要定义为private的。
父类中的访问限定符表示父类愿不愿意让子类继承,而继承方式则可以让子类缩小父类成员的访问权限,但不能放大父类成员的访问权限。
父类成员/子类继承方式 | public继承 | protected继承 | private继承 |
---|---|---|---|
父类的public成员 | 变为子类的public成员 | 变为子类protected成员 | 变为子类的private成员 |
父类的protected成员 | 变为子类的protected成员 | 变为子类的protected成员 | 变为子类的private成员 |
父类的private成员 | 子类不可见 | 子类不可见 | 子类不可见 |
总结
-
基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面
都不能去访问它。 -
基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
-
实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式等于
Min{成员在基类的访问限定符,继承方式}
,其中,public>protected>private。 -
使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
-
在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。
针对于总结中的第一点,父类private成员实际上还是被子类继承了,只是子类无法访问,下面使用代码验证↓↓↓
#include <iostream>
using namespace std;
class Base
{
private:
int _base;
};
class Son : public Base
{
}
int main()
{
Son s;
return 0;
}
基类和派生类对象赋值转换
●派生类对象可以赋值给基类的对象/基类的指针/基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。
下面给出代码示例↓↓↓
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
}
Person(const string& name, const char& sex, const int& age)
:_name(name)
,_sex(sex)
,_age(age)
{
}
protected:
string _name;
char _sex;
int _age;
};
class Student : public Person
{
public:
Student(const string& name, const char& sex, const int& age, const int& stuId)
:Person(name, sex, age)
,_stuId(stuId)
{
}
private:
int _stuId;
};
int main()
{
Student s("Jammingpro", 'M', 18, 123456);
Person p;
p=s;
return 0;
}
从监视窗口可以看到,Person对象保存了Student对象的父类成员部分,而舍弃了子类自有成员,这就是切片。
●基类对象不能赋值给派生类对象。(基类对象无法用于构造派生类对象,也无法使用派生类对象的拷贝赋值函数;但可以显示提供派生类赋值给基类的operator=实现)
★ps:由于派生类中的成员函数、成员对象一般情况下都会多于基类,如果基类直接赋值给派生类会导致部分成员数值不确定。因此,C++默认不提供基类赋值给派生类。
●基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针指向派生类对象时才是安全的。
下面代码演示了基类赋值给派生类指针,派生类赋值给基类指针↓↓↓
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
}
Person(const string& name, const char& sex, const int& age)
:_name(name)
, _sex(sex)
, _age(age)
{
}
string _name;
char _sex;
int _age;
};
class Student : public Person
{
public:
Student(const string& name, const char& sex, const