继承之概念
C++中的继承是面向对象编程(OOP)的一个核心概念,它允许一个类(派生类)继承另一个类(基类)的属性和方法。这使得在保持代码重用性的同时,还能实现代码的逻辑分层和扩展。
基本概念
基类(Base Class):被继承的类,有时也称为父类或超类。
派生类(Derived Class):继承基类的类,有时也称为子类。
访问修饰符:C++中有三种访问修饰符,分别是public
、protected
和private
,它们决定了类成员(包括继承的成员)的访问权限。
继承类型
公有继承(Public Inheritance):派生类继承基类的公有成员和保护成员(以及私有成员,但不可见),基类的公有成员在派生类中保持公有,保护成员保持保护。
保护继承(Protected Inheritance):派生类继承基类的公有成员和保护成员(以及私有成员,但不可见),基类的公有和保护成员在派生类中都变成保护成员。
私有继承(Private Inheritance):派生类继承基类的公有成员和保护成员(以及私有成员,但不可见),基类的公有和保护成员在派生类中都变成私有成员。
继承类型之探究
基类的其他成员在子类的访问方式,等价于MIN
(成员在基类的访问限定符,继承方式),public
>protected
>private
。
访问限定符public
,表示类内、类外、派生类都可以访问。访问限定符protected
,表示类内、派生类可以访问。访问限定符private
表示类内可以访问。
因此我们对于访问限定符可访问的范围可定义public
>protected
>private
。
如果继承方式是公有继承,即用public
修饰,那么基类的公有成员在派生类中保持公有,保护成员保持保护。
如果继承方式是保护继承,即用protected
修饰,那么基类的公有和保护成员在派生类中都变成保护成员。
如果继承方式是私有继承,即用private
修饰,那么基类的公有和保护成员在派生类中都变成私有成员。
C++中的三种访问限定符
C++中的访问修饰符public
、protected
和private
定义了类成员(包括属性和方法)的访问权限。这些访问权限控制了类成员在类的内部、派生类中以及类的对象中的可见性和可访问性。
public
访问修饰符
定义:public
成员在任何地方都是可访问的,无论是类的内部还是外部或者派生类。
使用场景:通常用于定义类的接口部分,即那些可以被类的对象安全访问和使用的公共方法和属性。
protected
访问修饰符
定义:protected
成员在类的内部和该类的派生类中是可访问的,但在类的对象(外部)中不可直接访问。
使用场景:用于那些允许在派生类中访问但不允许类的外部直接访问的成员。这在设计一个预期会被其他类继承的类时特别有用。
private
访问修饰符
定义:private
成员只能被其所在类的成员函数访问,包括该类的友元函数,但不能在类的外部或者派生类中访问。
使用场景:用于隐藏类的实现细节和保护类的状态,避免外部直接访问。
继承之示例
/*继承*/
#if 1
#include <iostream>
using namespace std;
class Person {
public:
void Print() {
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
private:
string _name = "peter";
int _age = 18;
};
class Student: public Person{
protected:
int _stuid;
};
class Teacher: public Person{
protected:
int _jobid;
};
int main(){
Student s;
Teacher t;
s.Print();
t.Print();
}
#endif
Person
类
Person
类包含两个私有成员变量:_name
和 _age
,分别表示人的名字和年龄。这些成员变量被初始化为 "peter"
和 18
。
提供了一个公有成员函数 Print
,用于输出 Person
对象的名字和年龄。
Student
类
Student
类是 Person
类的派生类,使用 public
继承方式。
添加了一个受保护成员变量 _stuid
,表示学生的学号。
Teacher
类
Teacher
类也是 Person
类的派生类,同样使用 public
继承方式。
添加了一个受保护成员变量 _jobid
,表示教师的工号。
main
函数
在 main
函数中,创建了一个 Student
对象 s
和一个 Teacher
对象 t
。
调用了这两个对象的 Print
方法,这两个方法实际上是继承自 Person
类的。由于 Student
和 Teacher
类没有重写 Print
方法,所以调用的是 Person
类中定义的版本。
继承之探究(何为不可见?)
我们之前说的“派生类继承基类的公有成员和保护成员”,这种说法似乎有一点问题。我们从继承示例代码运行的结果得出,Student
和Teacher
类都可以正常使用Print()
函数,而Print()
函数输出了_name
和_age
成员变量。因此Student
和Teacher
类对象中,是存在_name
和_age
成员变量的。因此派生类应该是继承了基类的公有成员和保护成员和私有成员。
但是在派生类中继承得到的私有成员,没有办法访问,是不可见的。这并不是意味着继承得到的私有成员访问限定符为private
。而是存在但不可见,不可见的意思是即使是访问this指针也没办法进行访问。
派生类没有办法用this
指针访问从基类继承得到的私有成员_name
和_age
。
基类可以通过this
指针访问私有成员变量_name
和_age
。
基类和派生类对象的赋值转换
基类指针或引用指向派生类对象
允许:派生类对象可以赋值给基类的指针或引用。这是多态的基础,允许使用基类指针或引用来指向派生类对象,并通过基类的接口来操作派生类对象。
class Base {};
class Derived : public Base {};
Derived d;
Base* bp = &d; // 基类指针指向派生类对象
Base& br = d; // 基类引用指向派生类对象
派生类指针或引用指向基类对象
不允许:基类对象不能直接赋值给派生类的指针或引用,因为基类对象可能不包含派生类所添加的成员。这样的赋值需要显式的类型转换,但是这样做是不安全的,除非通过某种机制(如动态类型识别)确保这种转换的有效性。
Base b;
Derived* dp = &b; // 错误:不能将基类对象的地址赋值给派生类指针
Derived& dr = b; // 错误:不能将基类对象赋值给派生类引用
派生类对象赋值给基类对象
允许:派生类对象可以赋值给基类对象。这种赋值会导致对象切片(Object Slicing),即派生类对象中超出基类部分的成员将被切除,只剩下基类部分的成员部分被赋值过去。这意味着赋值后,基类对象不会保有派生类特有的信息。
Derived d;
Base b = d; // 派生类对象赋值给基类对象,发生对象切片
基类对象赋值给派生类对象
不允许:通常情况下,基类对象不能直接赋值给派生类对象,因为派生类可能包含更多的信息,而基类对象无法提供这些额外信息的初始化。
基类和派生类对象的赋值转换之示例
子类对象可以赋值给父类对象/指针/引用
/*子类对象可以赋值给父类对象/指针/引用*/
#if 1
#include<iostream>
using namespace std;
class Person{
protected:
string _name;
string _sex;
int _age;
};
class Student: public Person{
public:
int _No;
};
int main(){
Student sobj;
Person pobj=sobj;
Person* pp=&sobj;
Person& rp=sobj;
}
#endif
基类对象不能赋值给派生类对象
/*基类对象不能赋值给派生类对象*/
#if 1
#include<iostream>
using namespace std;
class Person{
protected:
string _name;
string _sex;
int _age;
};
class Student: public Person{
public:
int _No;
};
int main(){
Student sobj;
Person pobj;
sobj=pobj;
}
#endif
基类的指针可以通过强制类型转换赋值给派生类的指针---派生类指针存放相符合地址
/*基类的指针可以通过强制类型转换赋值给派生类的指针*/
#if 1
#include<iostream>
using namespace std;
class Person{
protected:
string _name;
string _sex;
int _age;
};
class Student: public Person{
public:
int _No;
};
int main(){
Student sobj;
Person* pp=&sobj;
Student* ps1=(Student*)pp;
ps1->_No=10;
}
#endif
基类的指针可以通过强制类型转换赋值给派生类的指针---派生类指针存放不相符合地址---越界访问
/*基类的指针可以通过强制类型转换赋值给派生类的指针---越界访问*/
#if 1
#include<iostream>
using namespace std;
class Person{
protected:
string _name;
string _sex;
int _age;
};
class Student: public Person{
public:
int _No;
};
int main(){
Person pobj;
Person* pp=&pobj;
Student* ps2=(Student*)pp;
ps2->_No=10;
}
#endif
基类和派生类对象的赋值转换之探究
指针和引用
对象
小结论
针对于指针和引用,看他指向的是什么数据类型的信息。允许在大范围内通过小范围的方式寻找信息,不允许在小范围内通过大范围的方式寻找信息。(除非你可以保证信息安全)
针对于对象,看他存储的信息多少,基类存储的信息少,派生类存储的信息多。允许信息多的对象赋值给信息少的对象,不允许信息少的对象赋值给信息多的对象。
结尾
最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!