深入浅出C++继承机制:从入门到实战
目录
深入浅出C++继承机制:从入门到实战
一、继承的基本概念
二、继承的访问控制
三、继承中的特殊关系
1. 基类与派生类的转换
2. 继承中的作用域
3. 派生类的默认成员函数
四、多继承与菱形继承
1. 多继承模型
2. 菱形继承问题
3. 虚继承解决方案
五、继承与组合的选择
六、继承实战技巧
七、总结
深入浅出C++继承机制:从入门到实战
面向对象编程中,继承是最强大的特性之一,也是C++程序员必须掌握的核心概念。今天我们就来全面解析C++继承机制,从基础概念到高级应用,让你彻底搞懂继承!
一、继承的基本概念
继承是面向对象程序设计使代码可以复用的最重要手段。想象一下,学校里有老师和学生,他们都有姓名、年龄、地址等共同属性,但又有各自独特的属性(老师有职称,学生有学号)。如果分别定义两个类,就会出现大量重复代码。
class Person {
protected:string _name = "张三"; // 姓名int _age = 18; // 年龄string _address; // 地址string _tel; // 电话
};class Student : public Person {
protected:int _stuid; // 学号
};class Teacher : public Person {
protected:string _title; // 职称
};
通过继承,我们可以把公共成员放到Person基类中,Student和Teacher都继承Person,这样就避免了重复定义,代码更加简洁优雅。
二、继承的访问控制
继承方式有三种:public、protected和private。它们决定了基类成员在派生类中的访问权限:
基类成员/继承方式 | public继承 | protected继承 | private继承 |
---|---|---|---|
public成员 | public | protected | private |
protected成员 | protected | protected | private |
private成员 | 不可见 | 不可见 | 不可见 |
几点重要说明:
- 基类private成员在派生类中无论以什么方式继承都是不可见的
- protected成员是专为继承设计的访问级别
- 实际开发中绝大多数情况使用public继承
class Base {
public:int publicVar;
protected:int protectedVar;
private:int privateVar;
};class Derived : public Base {// publicVar仍然是public// protectedVar仍然是protected// privateVar不可见
};
三、继承中的特殊关系
1. 基类与派生类的转换
Student sobj;
// 派生类对象可以赋值给基类的指针/引用(切片)
Person* pp = &sobj;
Person& rp = sobj;// 基类对象不能赋值给派生类对象(编译报错)
// sobj = pobj;
2. 继承中的作用域
当基类和派生类有同名成员时,派生类成员会隐藏基类成员:
class Base {
public:void func() { cout << "Base::func()" << endl; }
};class Derived : public Base {
public:void func() { cout << "Derived::func()" << endl; }// 如果要访问基类的func,需要使用Base::func()
};
3. 派生类的默认成员函数
派生类的构造/析构等特殊成员函数有一些特殊规则:
class Person {
public:Person(const char* name = "peter") : _name(name) {}// ...
};class Student : public Person {
public:Student(const char* name, int num): Person(name) // 必须调用基类构造函数, _num(num) {}// ...
};
四、多继承与菱形继承
1. 多继承模型
class Worker {// 工人特有属性和方法
};class Student {// 学生特有属性和方法
};class WorkingStudent : public Worker, public Student {// 既是工人又是学生
};
2. 菱形继承问题
菱形继承是多继承的一种特殊情况,会导致数据冗余和二义性:
class Person {string _name;
};class Student : public Person {int _num;
};class Teacher : public Person {int _id;
};class Assistant : public Student, public Teacher {// 这里Person的成员会有两份!
};
3. 虚继承解决方案
使用虚继承可以解决菱形继承问题:
class Person {string _name;
};class Student : virtual public Person {int _num;
};class Teacher : virtual public Person {int _id;
};class Assistant : public Student, public Teacher {// 现在_name只有一份了
};
五、继承与组合的选择
在设计类关系时,需要慎重考虑使用继承还是组合:
// 继承(is-a关系)
class BMW : public Car {void Drive() { cout << "好开-操控" << endl; }
};// 组合(has-a关系)
class Stack {vector<int> _v; // 使用组合而非继承
};
一般原则:
- 如果确实是"is-a"关系,使用继承
- 如果是"has-a"关系,使用组合
- 当两者都适用时,优先考虑组合
六、继承实战技巧
- final关键字:C++11引入final关键字,可以禁止类被继承
class Base final {// 这个类不能被继承
};
- 静态成员继承:基类的静态成员在整个继承体系中只有一份
class Base {
public:static int count;
};
int Base::count = 0;class Derived : public Base {// 共享同一个count
};
- 友元关系不继承:基类的友元不能访问派生类的私有成员
七、总结
继承是C++面向对象编程的核心特性,合理使用可以:
- 提高代码复用性
- 建立清晰的类层次结构
- 实现多态(配合虚函数)
但也要注意:
- 避免过度使用继承
- 警惕菱形继承问题
- 在继承和组合之间做出明智选择
掌握好继承机制,你的C++面向对象设计能力将更上一层楼!