一.继承的定义与概念
1.定义:继承是一种is-a的关系,例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物,等等。
2.允许我们在保持原有类特性的基础上进行扩展,增加方法(成员函数) 和 属性(成员变量),这样产生新的类,称派生类
3.定义格式:
例:基类(也称父类)
Person
:class Person { public:void Print() {cout << "name:" << _name << endl;cout << "age:" << _age << endl;} protected:string _name = "peter"; // 姓名int _age = 18; // 年龄 };
从这个基类派生出
Student
类(也称子类):class Student : public Person { protected:int _stuid; // 学号 };
3.访问限定
不同的访问限定符决定了基类成员在派生类中的可见性和访问权限。以
public
继承为例,基类的public
成员在派生类中依然是public
,基类的protected
成员在派生类中仍是protected
,而基类的private
成员在派生类中不可见。
4.继承方式
二.基类和派生类对象赋值转换
派生类对象可以赋值给基类的对象、指针或引用,这一过程形象地称为 “切片” 或 “切割”。
将基类对象赋值给派生类对象是不被允许的,因为基类对象可能缺少派生类特有的成员。
class Person { protected:string _name; // 姓名string _sex; // 性别int _age; // 年龄 };class Student : public Person { public:int _No; // 学号 };void Test() {Student sobj;// 1. 子类对象可以赋值给父类对象/指针/引用Person pobj = sobj;Person* pp = &sobj;Person& rp = sobj;// 2. 基类对象不能赋值给派生类对象// sobj = pobj; // 错误,会导致编译错误// 3. 基类的指针可以通过强制类型转换赋值给派生类的指针,但需谨慎pp = &sobj;Student* ps1 = (Student*)pp; // 这种情况转换时可以的,因为 pp 原本就指向 Student 对象ps1->_No = 10;pp = &pobj;Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但会存在越界访问的问题,因为 pp 指向的是 Person 对象,不是 Student 对象ps2->_No = 10; // 可能会引发运行时错误 }
三.继承中的作用域
当子类和父类存在同名成员时,子类成员会屏蔽父类对同名成员的直接访问,这种现象称为隐藏或重定义
class Person { protected:string _name = "小李子"; // 姓名int _num = 111; // 身份证号 };class Student : public Person { public:void Print() {cout << "姓名:" << _name << endl;cout << "身份证号:" << Person::_num << endl; // 通过基类名限定访问基类的同名成员cout << "学号:" << _num << endl;} protected:int _num = 999; // 学号,与 Person 类中的 _num 同名 };void Test() {Student s1;s1.Print(); }
在
Student
类的_num
,则访问的是Student
类自身的_num
(学号)。若要访问基类Person
的_num
(身份证号),需要使用Person::_num
的方式进行显示访问
此外,对于成员函数的隐藏,只要函数名相同就会构成隐藏,即使函数参数列表不同。例如:
class A { public:void fun() {cout << "func()" << endl;} };class B : public A { public:void fun(int i) {A::fun(); // 调用基类的 fun 函数cout << "func(int i)->" << i << endl;} };void Test() {B b;b.fun(10); }
在
B
类中定义了fun(int i)
函数,它隐藏了基类A
中的fun
函数。在B
类的fun(int i)
函数中,可以通过A::fun()
来调用基类的fun
函数
四.派生类的默认成员函数
派生类的构造函数必须调用基类的构造函数来初始化基类部分的成员。如果基类没有默认构造函数,则必须在派生类构造函数的初始化列表中显式调用
class Person { public:Person(const char* name = "peter"): _name(name) {cout << "Person()" << endl;}Person(const Person& p): _name(p._name) {cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p) {cout << "Person operator=(const Person& p)" << endl;if (this!= &p)_name = p._name;return *this;}~Person() {cout << "~Person()" << endl;} protected:string _name; // 姓名 };class Student : public Person { public:Student(const char* name, int num): Person(name), _num(num) { // 调用基类构造函数初始化基类成员cout << "Student()" << endl;}Student(const Student& s): Person(s), _num(s._num) { // 调用基类拷贝构造函数初始化基类成员cout << "Student(const Student& s)" << endl;}Student& operator=(const Student& s) {cout << "Student& operator=(const Student& s)" << endl;if (this!= &s) {Person::operator=(s); // 调用基类赋值运算符函数初始化基类成员_num = s._num;}return *this;}~Student() {cout << "~Student()" << endl;} protected:int _num; // 学号 };void Test() {Student s1("jack", 18);Student s2(s1);Student s3("rose", 17);s1 = s3; }
注意:
Student(const char* name, int num): Person(name), _num(num) { // 调用基类构造函数初始化基类成员cout << "Student()" << endl;}
在初始化成员列表中,先调用了基类的构造函数,然后对派生类的成员进行初始化
构造函数与析构函数的顺序
五.继承与友元
友元关系不能继承。这意味着基类的友元函数不能访问派生类的私有和保护成员
class Student;class Person { public:friend void Display(const Person& p, const Student& s); protected:string _name; // 姓名 };class Student : public Person { protected:int _stuNum; // 学号 };void Display(const Person& p, const Student& s) {cout << p._name << endl;// cout << s._stuNum << endl; // 错误,友元函数不能访问派生类的私有成员 }void main() {Person p;Student s;Display(p, s); }
Display
函数是Person
类的友元函数,它可以访问Person
类的私有成员_name
,但不能访问Student
类的私有成员_stuNum
,即使Student
类继承自Person
类。
六.继承与静态成员
当基类定义了静态成员时,整个继承体系中只有一个该静态成员的实例。无论派生出多少个子类,它们都共享这一静态成员
class Person { public:Person() { ++_count; } protected:string _name; // 姓名 public:static int _count; // 统计人的个数 };int Person::_count = 0;class Student : public Person { protected:int _stuNum; // 学号 };class Graduate : public Student { protected:string _seminarCourse; // 研究科目 };void TestPerson() {Student s1;Student s2;Student s3;Graduate s4;cout << "人数 :" << Person::_count << endl; // 输出 4,因为共创建了 4 个 Person 或其派生类对象Student::_count = 0; // 可以通过派生类名访问静态成员,但实际上修改的是基类的静态成员cout << "人数 :" << Person::_count << endl; // 输出 0 }
Person
类的静态成员_count
用于统计创建的Person
及其派生类对象的数量。无论是通过Person
类还是Student
类来访问_count
,都是同一个静态成员变量。
参考了这位大佬的文章,并结合自己的情况进行了总结
A charmer-CSDN博客A charmer擅长C++,数据结构,算法,等方面的知识,A charmer关注c++,c语言,linux,数据结构领域.https://charmer.blog.csdn.net/