1. 继承的概念
继承(Inheritance)是面向对象程序设计中实现代码复用的核心机制。它允许在保持原有类特性的基础上进行扩展,增加新的成员函数和成员变量,由此产生的新类称为派生类(Derived Class),原有类称为基类(Base Class)。
若不采用继承机制,设计具有相似属性的类时会导致代码冗余。例如 Student 和 Teacher 类都包含姓名、地址、年龄、电话等成员变量,以及身份认证函数 identity():
class Student {
public:
void identity() { /* 身份认证 */ }
void study() { /* 学习 */ }
protected:
string _name = "peter";
string _address;
string _tel;
int _age = 18;
int _stuid;
};
class Teacher {
public:
void identity() { /* 身份认证 */ }
void teaching() { /* 授课 */ }
protected:
string _name = "张三";
int _age = 18;
string _address;
string _tel;
string _title;
};
通过引入公共基类 Person,让 Student 和 Teacher 继承 Person,即可复用公共成员,消除冗余:
class Person {
public:
void identity() { cout << "void identity() " << _name << endl; }
protected:
string _name = "张三";
string _address;
string _tel;
int _age = 18;
};
class Student : public Person {
public:
void study() { /* ... */ }
protected:
int _stuid;
};
class Teacher : public Person {
public:
void teaching() { /* ... */ }
protected:
string title;
};
int main() {
Student s;
Teacher t;
s.identity();
t.identity();
return 0;
}
2. 继承的定义
2.1 定义格式
基类(Base Class)也称父类,派生类(Derived Class)也称子类。继承的语法格式如下:


2.2 基类成员访问方式的变化
继承方式决定了基类成员在派生类中的访问权限。具体变化如下表所示:
| 基类成员 / 继承方式 | public 继承 | protected 继承 | private 继承 |
|---|---|---|---|
| public 成员 | 派生类的 public 成员 | 派生类的 protected 成员 | 派生类的 private 成员 |
| protected 成员 | 派生类的 protected 成员 | 派生类的 protected 成员 | 派生类的 private 成员 |
| private 成员 | 在派生类中不可见 | 在派生类中不可见 | 在派生类中不可见 |
核心规则总结:
- 基类的
private成员无论以何种方式继承,在派生类中均不可见。这里的'不可见'是指语法上限制派生类(无论在类内还是类外)直接访问它,但该成员仍会被继承到派生类对象中。 - 若基类成员不希望被类外直接访问,但需要在派生类中访问,应将其定义为
protected。保护成员限定符正是为继承机制而设计的。 - 派生类成员的最终访问权限遵循取小原则:
派生类访问权限 = min(基类成员访问权限, 继承方式)(权限等级:public > protected > private)。 - 使用
class关键字时默认继承方式为private,使用struct时默认为public。为提高代码可读性,建议始终显式指定继承方式。 - 实际开发中绝大多数情况使用
public继承。protected和private继承会限制成员的访问范围,降低代码的扩展性与可维护性,通常不推荐使用。
3. 继承模板
当基类为类模板时,派生类在调用基类成员函数时需要显式指定类域,否则可能因模板的'按需实例化'特性导致编译错误。
namespace jiang {
template <class T>
class stack : public std::vector<T> {
public:
void push(const T& x) {
// 基类是类模板时,必须指定类域,否则编译报错
// 因为 stack<int> 实例化时也会实例化 vector<int>
// 但模板按需实例化,push_back 等成员函数此时未实例化,直接调用会找不到标识符
vector<T>::push_back(x);
}
void pop() { vector<T>::pop_back(); }
const T& top() { return vector<T>::back(); }
bool empty() { return vector<T>::empty(); }
};
}
int main() {
jiang::stack<int> st;
st.push(1);
st.push(2);
st.push(3);
while (!st.empty()) {
cout << st.top() << " ";
st.pop();
}
return 0;
}
4. 基类与派生类对象之间的转换
- 派生类对象赋值给基类指针/引用:
public继承的派生类对象可以隐式转换为基类的指针或引用。这一过程形象地称为**切片(Slicing)**或切割,即只提取派生类中属于基类的那部分数据,基类指针或引用指向该部分。 - 基类对象赋值给派生类对象:不允许隐式转换,编译器会直接报错。
- 基类指针/引用转换为派生类指针/引用:可以通过强制类型转换实现,但仅当基类指针实际指向派生类对象时才是安全的。若基类为多态类型,推荐使用 RTTI(Run-Time Type Information)中的
dynamic_cast进行安全的向下转换。

class Person {
protected:
string _name;
string _sex;
int _age;
};
class Student : public Person {
public:
int _No;
};
int main() {
Student sobj;
// 1. 派生类对象可以赋值给基类的指针/引用
Person* pp = &sobj;
Person& rp = sobj;
// 派生类对象赋值给基类对象时,会调用基类的拷贝构造函数完成切片
Person pobj = sobj;
// 2. 基类对象不能赋值给派生类对象(编译报错)
// sobj = pobj;
return 0;
}


