目录
编辑
本节学习目标
1. 继承的概念及定义
1.1 什么是继承
1.2 继承定义语法
基类成员在派生类的访问规则
1.3 继承模板类(模板父类)
2. 基类与派生类对象转换(切片 / 切割 slicing)
3. 继承体系下作用域与隐藏(name‑hiding)
同名成员变量隐藏示例
同名成员函数隐藏(重点选择题)
4. 派生类的默认 6 个成员函数
派生类默认成员函数执行规则
4.2 禁止类被继承两种写法
5. 继承与友元
6. 继承与静态成员
7. 多继承、菱形继承与虚继承 virtual
7.1 单继承、多继承
7.2 菱形继承(钻石继承)—— 多继承特殊坑
7.3 虚继承 virtual 解决菱形继承
8. 继承与组合
8.1 is‑a vs has‑a
📔本篇核心考点总结
本节学习目标
1. 继承的概念及定义
1.1 什么是继承
继承是面向对象实现代码复用最重要机制。 在保留原有类(基类 / 父类)全部特性的基础之上,扩展出新的类(派生类 / 子类)。
场景:学生 Student、老师 Teacher。两者都包含姓名、地址、电话、年龄、身份认证identity();学生独有学号、学习;老师独有职称、授课。 不使用继承,两份类中重复写相同成员,代码大量冗余。
把公共属性、方法抽出来放到Person父类;Student、Teacher继承 Person,直接复用父类成员,只写自己独有的内容。
#include <iostream>
#include <string>
using namespace std;
//父类(基类)
class Person
{
public:
void identity()
{
cout << "void identity() " << _name << endl;
}
protected:
string _name = "张三";
string _address;
string _tel;
int _age = 18;
};
//子类(派生类) public继承Person
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;
}
1.2 继承定义语法



class 派生类名 : 继承方式 基类名
{
//派生类自己新增的成员
};
继承三种方式:public、protected、private
基类成员在派生类的访问规则
注意:不是没有继承到子类对象中,只是语法禁止访问,对象内存里面依然存在这份数据。 如果希望子类可以访问,但是外部不能访问,成员设置为protected,protected 就是为继承场景诞生。
表格
| public | public | protected | private |
| protected | protected | protected | private |
| private | 不可见 | 不可见 | 不可见 |
- 使用class定义类,默认继承方式是 private;
- 使用struct定义类,默认继承方式是 public;
- ✅工程开发绝大多数场景只用public继承;protected/private继承极少使用,扩展性差。
示例演示三种继承:
class Person
{
public:
void Print() { cout << _name << endl; }
protected:
string _name;
private:
int _age;
};
// class Student : protected Person;
// class Student : private Person;
class Student : public Person
{
protected:
int _stunum;
};
1.3 继承模板类(模板父类)
如果父类是类模板,子类继承的时候,必须带上基类模板域,否则编译器找不到基类成员。 例:stack 继承 vector,模拟栈。
namespace bit
{
template<class T>
class stack : public std::vector<T>
{
public:
void push(const T& x)
{
// 必须写 vector<T>::push_back(x);
// push_back(x); //直接写会编译报错,模板按需实例化找不到名字
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()
{
bit::stack<int> st;
st.push(1);
st.push(2);
st.push(3);
while(!st.empty())
{
cout << st.top() << " ";
st.pop();
}
return 0;
}
2. 基类与派生类对象转换(切片 / 切割 slicing)

只针对public 公有继承
切片(切割):把派生类对象里面属于基类的那一部分切出来,交给基类。派生类独有的成员直接被切掉丢弃。
class Person
{
protected:
string _name;
string _sex;
int _age;
};
class Student : public Person
{
public:
int _No; //学号
};
int main()
{
Student sobj;
//1.派生类 → 基类指针
Person* pp = &sobj;
//2.派生类 → 基类引用
Person& rp = sobj;
//3.派生类赋值给基类对象(发生对象切片,调用基类拷贝构造)
Person pobj = sobj;
// ❌基类对象不能直接赋值给派生类对象,编译报错
// sobj = pobj;
return 0;
}
- 基类普通对象不能直接赋值给派生类对象。
- 基类指针如果本来指向派生类对象,可以强制转换为派生类指针;安全转换后面多态章节学习dynamic_cast。
3. 继承体系下作用域与隐藏(name‑hiding)
注意:只要函数名字一样就构成隐藏,参数不一样也隐藏,不是重载!重载只发生在同一个作用域!
工程编码建议:尽量不要写同名成员,可读性差容易踩坑。
同名成员变量隐藏示例
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; //学号,和父类_num同名,发生隐藏
};
int main()
{
Student s1;
s1.Print();
return 0;
}
同名成员函数隐藏(重点选择题)
class A
{
public:
void fun()
{
cout << "A::fun()" << endl;
}
};
class B : public A
{
public:
//函数名字相同,参数不同,构成【隐藏】,不是重载!
void fun(int i)
{
cout << "B::fun(int i)" << i << endl;
}
};
int main()
{
B b;
b.fun(10); //调用B::fun(int)
// b.fun(); //编译报错,子类作用域把父类fun()隐藏了!
b.A::fun(); //想要调用父类版本,必须显式指定类域
return 0;
}
考点:重载必须同一个类同一个作用域;父子类之间同名函数,不管参数是否一样,都是隐藏。
4. 派生类的默认 6 个成员函数

编译器在满足条件时自动生成 6 个默认成员函数:构造、拷贝构造、赋值运算符重载、析构、取地址两个重载。
派生类默认成员函数执行规则

对象构造顺序:先基类构造,再派生类构造。
对象析构顺序:先派生类析构,再基类析构,和构造顺序相反。
完整示例代码:
#include <iostream>
#include <string>
using namespace std;
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)
{
//显式调用基类赋值重载,派生类会隐藏基类operator=
Person::operator=(s);
_num = s._num;
}
return *this;
}
~Student()
{
cout << "~Student()" << endl;
//编译器自动在函数末尾调用 ~Person(),不需要手写
}
protected:
int _num;
};
int main()
{
Student s1("jack",18);
Student s2(s1);
Student s3("rose",17);
s1 = s3;
return 0;
}
4.2 禁止类被继承两种写法
class Base final
{
public:
void func5() { cout << "Base::func5" << endl; }
protected:
int a = 1;
};
// class Derive : public Base{}; //编译报错,Base被final禁止继承
5. 继承与友元
❗友元关系不会继承。基类的友元函数,不能直接访问派生类的 protected/private 成员。
#include <iostream>
#include <string>
using namespace std;
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; //编译报错!友元不继承,不能访问Student的_stuNum
}
int main()
{
Person p;
Student s;
// Display(p,s); //报错
return 0;
}
解决:需要把 Display 也设置为 Student 的友元。
6. 继承与静态成员
基类里面的 static 静态成员,整个继承体系之内只有唯一一份,所有派生类对象共享同一份静态成员。 可以通过基类名、派生类名访问 public 静态成员。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
string _name;
static int _count;
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum;
};
int main()
{
Person p;
Student s;
cout << &p._name << endl; //普通成员每个对象独立一份,地址不同
cout << &s._name << endl;
cout << &p._count << endl;
cout << &s._count << endl; //静态成员地址完全相同,共享同一份
cout << Person::_count << endl;
cout << Student::_count << endl;
return 0;
}
7. 多继承、菱形继承与虚继承 virtual
7.1 单继承、多继承
- 单继承:一个派生类只有一个直接基类。

- 多继承:一个派生类拥有两个及以上直接基类。

对象内存排布:继承列表从左到右,先继承的基类放在内存低地址,后继承放后面,派生类自己成员放最后。
7.2 菱形继承(钻石继承)—— 多继承特殊坑


继承关系:Assistant助理同时继承Student和Teacher;Student、Teacher 都继承 Person。
问题:
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
string _name;
};
class Student : public Person
{
protected:
int _num; //学号
};
class Teacher : public Person
{
protected:
int _id; //职工编号
};
//助理,多继承 Student、Teacher
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse;
};
int main()
{
Assistant a;
// a._name = "peter"; //编译报错,二义性!两份_name不知道选哪一个
//强制指定路径可以访问,但是不能解决数据冗余
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
return 0;
}
7.3 虚继承 virtual 解决菱形继承
在中间层继承的时候写virtual public Person虚继承;公共父类 Person 在子类对象里面只保存唯一一份,消除冗余和二义性。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
string _name;
};
//虚继承
class Student : virtual public Person
{
protected:
int _num;
};
//虚继承
class Teacher : virtual public Person
{
protected:
int _id;
};
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse;
};
int main()
{
Assistant a;
a._name = "peter"; //不再二义性,只有唯一一份Person
return 0;
}
⚠️注意:虚继承会引入虚基表,底层实现复杂,有一定性能开销;工程尽量不要设计菱形继承。Java 直接取消多继承从根源规避这个问题。
扩展:C++ 标准 IO 库ios_base就是典型虚继承菱形结构。

8. 继承与组合
8.1 is‑a vs has‑a
Student is‑a Person;BMW is‑a Car。 继承属于白箱复用:基类内部细节对子类可见;父子类耦合度很高,基类改动会强烈影响子类。
Car has‑a Tire(汽车拥有轮胎)。把别的类作为自己的成员变量。 组合属于黑箱复用,只使用对方公开接口,内部细节不可见;耦合低,维护性更好。
✨开发经验:优先使用组合;只有满足 is‑a 语义再用继承;实现多态必须使用继承。
示例对比 stack 两种实现:
//方式1:继承 vector is‑a(不推荐)
template<class T>
class stack : public std::vector<T>
{};
//方式2:组合 has‑a(推荐,容器适配器真实实现思路)
template<class T>
class stack
{
public:
std::vector<T> _v;
};
📔本篇核心考点总结
面试简答
Q:基类 private 成员子类能不能继承?能不能访问?
A:内存上继承到子类对象,但是语法不可访问;子类需要访问就设置为 protected。
Q:成员隐藏和重载区别?
A:重载在同一个作用域,函数名相同参数不同;父子不同作用域只要名字相同就是隐藏,和参数无关。
Q:菱形继承问题怎么解决?
A:中间层使用 virtual 虚继承;最好设计上避免菱形继承。
Q:继承和组合怎么选?
A:is‑a 是继承;has‑a 优先组合,组合耦合更低。

网硕互联帮助中心





评论前必须登录!
注册