【本节内容】
继承的概念及定义
基类和派生类对象赋值转换
1. 继承
1.1概念
继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生的类叫做派生类(子类),原有类叫做基类(父类)。
即下文所有对象关系 基类——派生类,父类——子类
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。之前我们解除的都是函数复用,而继承属于类设计层次的复用。
class Person
{
public:
void Print()
{
cout << "_name" << “ ”;
cout << "_age" << endl;
}
protected:
string _name = "ly";
int _age = 21;
};
class Student :public Person
{
protected:
int _id;
};
class Teacher :public Person
{
protected:
int _tid;
};
int main()
{
Student s;
Teacher t;
s.Print();
t.Print();
system("pause");
return 0;
}
由 s.Print(); t.Print();
结果打印出两遍 _name _age可知,子类复用了父类的成员。
即在子类继承父类后,父类的成员(成员函数+成员变量)都会变成子类的一部分。
1.2 定义
1.由上面代码可知,继承的定义格式是:
2.继承方式和访问限定符
3.继承基类成员访问方式的变化
基类成员\继承方式 | public继承 | protected继承 | private继承 |
---|---|---|---|
public成员 | 派生类public成员 | 派生类protected成员 | 派生类private成员 |
protected成员 | 派生类protected成员 | 派生类protected成员 | 派生类private成员 |
private成员 | 在派生类不可见 | 在派生类不可见 | 在派生类不可见 |
总结:
1)父类的私有成员无论以什么方式继承到子类中,都是不可见的。
这里的不可见指的是父类私有成员仍然继承到了派生类中,只是语法上限定派生类对象无法访问这些成员。
2)父类的public成员和protect成员在子类中的访问方式==Min(成员在父类的访问限定符,继承方式),其中public>protected>private
3)如果父类的成员不想被类外直接访问,但需要在派生类中访问,可以将父类的成员定义为protected。这里可以看出protected 成员限定符是因继承才出现的。
4)使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public。虽然有默认的继承方式,但是我们最好显示写出继承方式。
5)在实际运用中,一般都使用public继承,很少用protected和private继承,因为后两者继承下来的成员只能在派生类里面使用,实际中的扩展维护性不强。
实例演示三种继承方式下基类成员访问关系的变化:
class Person
{
public:
void Print() { cout << _name << endl; }
protected:
string _name = "ly"; int _age = 21;
private:
string _sex ="女";
};
class Student :public Person //public 继承下
{
public:
void Display()
{
Print(); //可以访问基类的public 成员函数
cout << _age << endl; //可以访问基类的protect 成员变量
//cout << _sex << endl; //不可访问基类的private成员变量!所以编译不通过!
cout << _id << endl; //可以访问派生类的protected 成员
}
protected:
int _id= 160;
};
2.基类和派生类对象赋值转换
1)派生类对象可以赋值给基类的对象/基类的指针/基类的引用。 这里的过程叫做切片或切割,寓意把派生类继承的基类部分切除赋值给基类。
2) 基类对象无法赋值给派生类对象。(基类中成员不够,导致非法访问)
3) 基类的指针可以通过强制类型转换赋值给派生类的指针。但是必须基类指针指向派生类对象时才是安全的,否则可能存在越界访问的问题。
实例演示:
class Person
{
protected:
string _name; //姓名
int _age; //年龄
string _sex; //性别
};
class Student :public Person
{
public:
int _id; //学号
};
void Test()
{
Student s;
//1)子类对象可以赋值给父类(切片)
Person p = s; //对象
Person *pp = &s; //指针
Person &rp = s; //引用
//2)基类对象不能赋值给派生类对象
//s = p; //编译不通过
//3)基类指针可以通过强制类型转换赋值给派生类指针
pp = &s; //父类指针pp原本指向子类
//pp->_id = 10; //此时pp无法访问子类的成员
Student *ps1 = (Student*)pp; //将父类指针强转为子类指针
ps1->_id = 10; //pp可以访问子类成员
pp = &p; //父类指针pp原本指向父类
Student *ps2 = (Student*)pp; //也可以转换成功,但会存在越界访问的问题
ps2->_id = 10;
}
注意:第三点中,将指向子类的父类指针pp强转为子类的结果是,指针pp由只能访问子类中父类的成员变为可访问子类中的所有成员的指针。
想对继承有更深理解的伙伴可以继续关注博主的以下几篇博客
继承(二):https://blog.youkuaiyun.com/ly_6699/article/details/88805464
讲到继承中的作用域和派生类的默认成员函数
继承(三):https://blog.youkuaiyun.com/ly_6699/article/details/88858186
讲到继承和友元,继承和静态成员,继承和组合
继承(四):https://blog.youkuaiyun.com/ly_6699/article/details/88858618
讲到复杂的菱形继承和菱形的虚拟继承