C++中的继承(中)
1..继承中的作用域继承体系中基类和派生类是两个相互独立的作用域派生类作用域嵌套在基类作用域内查找成员时优先找自己的1.两个独立作用域基类父类有自己的作用域派生类子类有独立的作用域不是同一个空间。2.成员查找顺序调用成员时 → 先在派生类作用域查找 → 找不到 → 再去基类作用域查找。3.同名成员隐藏重定义只要派生类和基类有同名成员变量 / 函数无论参数是否相同派生类成员会直接隐藏基类的同名成员这是作用域导致的和重载无关我们用最简的 Base基类和 Derived派生类演示1.同名成员变量隐藏#include iostream using namespace std; // 基类作用域 class Base { public: int m_A 10; // 基类成员 }; // 派生类作用域独立 class Derived : public Base { public: int m_A 20; // 与基类同名隐藏基类的m_A }; int main() { Derived d; // 优先找派生类自己的成员 cout d.m_A endl; // 输出20子类的m_A // 强制访问基类被隐藏的成员作用域解析符 :: cout d.Base::m_A endl; // 输出10基类的m_A return 0; }2.同名成员函数关键哪怕函数参数不同只要同名就会隐藏不是重载重载必须在同一个作用域class Base { public: void show() { cout 基类无参show endl; } }; class Derived : public Base { public: // 同名函数参数不同 → 依然隐藏基类的show() void show(int a) { cout 派生类有参show endl; } }; int main() { Derived d; d.show(10); // ✅ 调用子类派生类有参show // d.show(); // ❌ 报错基类无参show被隐藏了找不到 // 强制调用基类被隐藏的函数 d.Base::show(); // ✅ 基类无参show }3、访问被隐藏的基类成员只要派生类隐藏了基类成员想访问基类原版必须用基类名成员名:: 是作用域解析符强制指定去基类作用域查找。4、静态成员的作用域静态成员也遵循作用域隐藏规则同名静态成员同样会被隐藏class Base { public: static int s; // 静态成员 }; int Base::s 10; class Derived : public Base { public: static int s; // 同名静态成员隐藏基类s }; int Derived::s 20; // 访问 cout Derived::s endl; // 20子类 cout Base::s endl; // 10基类2.派生类的默认成员函数派生类的 6 个默认成员函数都会自动调用基类的对应默认成员函数派生类只负责处理自己的成员基类成员交给基类的函数处理1. 构造函数1.生成派生类默认构造 → 自动调用基类无参构造2.顺序先调用基类构造 → 再执行派生类构造3.大坑如果基类没有无参构造只有有参构造派生类必须显式调用基类有参构造否则编译报错class Person { public: // 基类只有有参构造无默认无参构造 Person(string name) : _name(name) { cout Person构造 endl; } protected: string _name; }; class Student : public Person { public: // 派生类构造必须显式调用基类有参构造 Student(string name, int id) : Person(name), _id(id) { cout Student构造 endl; } private: int _id; }; // 调用 Student s(张三, 1001); // 输出Person构造 → Student构造2. 拷贝构造函数1.生成派生类默认拷贝构造 → 自动调用基类拷贝构造2.作用完成基类部分 派生类部分的完整拷贝3.语法编译器自动生成Derived(const Derived d) : Base(d) { ... }// 默认生成的拷贝构造等价于 Student(const Student s) : Person(s), _id(s._id) { cout Student拷贝构造 endl; } // 使用 Student s1(李四, 1002); Student s2 s1; // 调用拷贝构造完整拷贝基类派生类成员3. 赋值运算符重载 (operator)1.生成派生类默认 operator → 自动调用基类 operator2.顺序先赋值基类部分 → 再赋值派生类部分3.新手必坑如果自己重写 operator必须手动调用基类的赋值函数否则基类成员不会被赋值// 自己重写赋值重载 Student operator(const Student s) { if (this s) return *this; // 1. 手动调用基类赋值重载必须写 Person::operator(s); // 2. 赋值派生类自己的成员 _id s._id; cout Student赋值重载 endl; return *this; }4. 析构函数1.生成派生类默认析构 → 自动调用基类析构2.顺序先执行派生类析构 → 再调用基类析构和构造完全相反3.注意基类析构函数建议加 virtual多态时防止内存泄漏后续学习// 析构函数调用顺序 ~Student() { cout Student析构 endl; // 编译器自动调用 ~Person() } // 销毁对象s时输出 // Student析构 → Person析构有小伙伴就要问这里的派生类析构先调用自己的析构再调用基类的析构就不会重复析构吗一个派生类对象本质是 一块连续的内存内存里只包含两部分✅ 基类成员部分✅ 派生类独有成员部分它不是两个独立的对象所以只会被释放一次内存根本不存在重复析构析构函数的职责只清理「自己负责的资源」分工明确互不重叠派生类析构清理自己新增成员的资源基类析构清理基类成员的资源各清各的完全不冲突#include iostream using namespace std; // 基类 class Person { public: Person() { cout Person构造 endl; } ~Person() { cout Person析构清理基类成员 endl; } }; // 派生类 class Student : public Person { public: Student() { cout Student构造 endl; } ~Student() { cout Student析构清理派生类成员 endl; } }; int main() { Student s; // 创建一个对象 // 生命周期结束自动销毁 return 0; }运行结果✅ 全程只有一个对象一次内存释放两个析构只是分阶段清理资源完全不是重复销毁对象谢谢