C++继承
一、继承基础
1.继承概念
继承允许新类(派生类)基于现有类(基类)创建,保留基类特性并扩展新功能。这实现了类设计层次的代码复用。
class Person {public: void Print() { cout << \"name:\" << _name << \"\\nage:\" << _age; }protected: string _name = \"peter\"; int _age = 18;};class Student : public Person { // public继承protected: int _stuid; // 新增学号成员,派生类Student基于现有类Person进行扩展};
在上述代码中,继承后基类Person的成员(成员函数和成员变量),都会变为派生类Student的一部分,体现了Student对Person的复用。
2.继承的本质
继承的本质是复用,将每个重复使用的模块剥离开,成为一个单独的模块(基类),需要使用这个模块时就直接拷贝到派生类中使用。
class Person{public:Person(){}void Print(){cout << \"name:\" << _name << endl;}string _name = \"peter\";};class Student : public Person{public:void func(){cout << \"name:\" << _name << endl;} void printid() { cout << _stuid << endl; }protected:int _stuid = 0000;//学号};int main(){Student s1;s1._name = \"Tom\";//修改继承的name不会改变基类中person中的值s1.func();//这输出TomPerson p;p.Print();//这输出Peter}
继承后,基类的成员会拷贝一份到派生类中,变成派生类的一部分。 从上述程序可以发现,修改派生类中的基类成员,不会改变原本的基类成员,说明,在继承中,派生类在结构上继承了原来的基类,但派生类实例化后,继承的基类与原来的基类不是同一个。
二、关键特性深度解析
1. 访问权限变化规则
核心规则:
- 基类private成员在任何继承中均不可直接访问
- 派生类访问权限 =
min(基类访问权限, 继承方式)
- 默认继承方式:
class
为private,struct
为public(建议显式声明)
2. 对象切片(赋值转换)
1.切片:派生类对象可以赋值给基类的对象/指针/引用 。
2.基类不能赋值给派生类。
Student sobj;Person pobj = sobj; // ✅ 派生类→基类(切片)// sobj = pobj; // ❌ 禁止基类→派生类Person* pp = &sobj; // ✅ 指针转换Student* ps1 = (Student*)pp; // 强制转换(需确保安全)int main(){Student s;Person* ptr = &s;//将student中person部分切片,ptr指向切片,ptr指向切片部分的头部,且只能访问派生类中基类的部分Person& ref = s;//ref引用s中person的部分,ptr指向切片部分的头部,且只能访问派生类中基类的部分ptr->_name = \"xxx\";cout << (*ptr)._name << endl;//ptr和ref指向的都是s中属于person区域的部分,修改ptr和ref指向区域的值,s也会改变。cout << s._name << endl; cout << ref._name << endl;ref._name = \"yyy\";cout << ref._name << endl;cout << s._name << endl;cout << (*ptr)._name << endl; //cout << ref.printid() << endl;//这里会报错,说明派生类到基类后,只能访问基类结构中包含的元素}
在上述程序中,将派生类赋值给基类的指针和引用,修改指针ptr,派生类和基类引用均会改变,修改引用ref,派生类和基类指针均会改变。说明切片的基类指针和引用,都指向派生类的那块地址。
3. 作用域与隐藏
同名成员会形成隐藏(重定义):
class Person {protected: int _num = 111; // 身份证号};class Student : public Person {public: void Print() { cout << Person::_num; // 显式访问基类成员 cout << _num; // 访问派生类成员 }protected: int _num = 999; // 学号(隐藏基类_num)};
重要:成员函数只需函数名相同即构成隐藏,与参数无关!
4. 派生类默认成员函数
class Student : public Person {public: // 构造函数必须调用基类构造 Student(const char* name, int num) : Person(name), _num(num) {} // 拷贝构造需调用基类拷贝构造 Student(const Student& s) : Person(s), _num(s._num) {} // 赋值运算符重载 Student& operator=(const Student& s) { if (this != &s) { Person::operator=(s); // 调用基类operator= _num = s._num; } return *this; } // 析构函数自动调用基类析构(先派生后基类) ~Student() { /* 自动调用~Person() */ }};
5.继承实现
//基类class Person{public:Person(const char* name = \" \")//默认构造函数:_name(name){cout << \"Person()\" << endl;cout << _name << 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;delete[] _str;//string是自定义类型,析构释,会自动调用他的析构,所以这里只需要手动释放_str的空间即可}void func(){cout << _name << endl;}protected:string _name;//姓名char* _str = new char[10] {\'x\', \'y\', \'z\'};};//派生类class Student :public Person{public:Student(const char* name = \" \", int x = 0, const char* address = \" \"):Person(name)//切片,派生类中初始化基类,要调用基类的默认构造,_x(x),_address(address),_name(Person::_name + \'x\'){cout << \"Student(const char* name = \" \", int x = 0, const char* address = \" \") \" << endl;}Student(const Student& st):Person(st)//调用基类的拷贝构造Person,_x(st._x),_address(st._address),_name(Person::_name){cout << \"Student(const Student& st)\" << endl;}Student& operator=(const Student& st){if (this != &st){Person::operator=(st);//调用基类的赋值_x = st._x;_address = st._address;}return *this;}~Student()//析构会自动调用基类的析构和自定义类型的析构{cout << \"~Student()\" << endl;cout << _str << endl;}protected:int _x = 1;string _address = \"厦门\";string _name;};int main(){Student s1;Student s2(\"张三\", 1, \"厦门集美\");return 0;}
三、继承VS组合
黄金准则:
- 优先使用组合(降低耦合)
- 仅当is-a关系明确时使用继承
- 避免多继承,严禁菱形继承
四、继承小知识点
1.继承权限:始终显式声明访问控制符(public/protected/private)和继承方式,避免依赖默认规则导致的意外行为。
2.基类私有成员会被继承但不可访问,这是C++封装性的核心设计。继承权限影响的是基类成员在派生类中的可见性,而基类对象自身的访问规则保持不变。
3.
- 同名隐藏的本质:派生类成员会隐藏基类所有同名成员(无论函数参数/变量类型)
- 访问被隐藏成员的方法:使用作用域解析符
BaseClass::member
- 在基类和派生类中,允许存在同名成员变量或函数(无论参数是否相同),此时派生类成员会隐藏基类同名成员。
4.当基类没有默认构造函数时,派生类必须在初始化列表中显式调用基类构造函数.
5.构造/析构顺序
当基类没有默认构造时,必须参考基类构造函数定义派生类构造。构造由外而内(基类→派生),析构由内而外(派生→基类)。派生类析构函数只需释放自身资源,基类析构由编译器自动调用完成。
6.友元关系是C++中唯一完全不可继承的关系。静态成员在继承体系中保持唯一实例,普通成员变量无论访问权限都会被继承(私有成员虽不可访问但占用内存)。