C++继承详解:基类、派生类与访问权限
一、什么是继承?
1. 继承的基本概念
继承是 C++ 面向对象编程中的重要特性,
它允许一个类在已有类的基础上获得其可继承的成员,并在此基础上扩展新的属性和行为,从而实现代码复用
例如,设计一个动物管理系统,其中包含狗、猫等不同动物
狗和猫虽然具有不同的特点,但它们都具有一些共同的属性和行为,例如名字、年龄、进食等
如果分别定义狗类和猫类,就会出现重复代码
因此,可以将它们的共同特征提取出来,定义一个动物类,再让狗类和猫类继承动物类
#include<iostream>
using namespace std;
//基类
class Animal
{
public:
string name;
void eat()
{
cout << name << "正在进食" << endl;
}
};
//派生类
class Dog:public Animal
{
public:
void bark()
{
cout << name << "正在叫" << endl;
}
};
class Cat:public Animal {
public:
void meow()
{
cout << name << "正在叫" << endl;
}
};
int main()
{
Dog dog;
dog.name = "小狗";
dog.eat();
dog.bark();
Cat cat;
cat.name = "小猫";
cat.eat();
cat.meow();
return 0;
}
运行结果:

可以看到,Dog 和 Cat 并没有重新定义 name 和 eat(),却可以直接使用它们
这是因为它们继承了 Animal 类
继承的核心思想:将多个类共有的属性和行为提取到一个基类中,派生类在此基础上扩展自己的功能,避免重复编写代码。
2. 继承和派生的区别
继承和派生实际上描述的是同一种类关系,只是观察角度不同。
| 继承(Inheritance) | 强调派生类获得基类的可继承成员 |
| 派生(Derivation) | 强调以已有类为基础定义一个新的类 |
| 基类(Base Class) | 被继承的类,也称父类 |
| 派生类(Derived Class) | 通过继承定义出来的类,也称子类 |
例如:
class Animal {};
class Dog : public Animal {};
从继承角度看:Dog 继承了 Animal
从派生角度看:Dog 是由 Animal 派生出来的
一句话记忆:继承强调获得,派生强调产生
二、C++ 继承的基本语法
1. 继承语法
C++ 使用冒号 : 实现继承
class 基类
{
//基类成员
};
class 派生类:继承方式 基类
{
//派生类新增的成员
};
例如:
class Animal
{
public:
void eat()
{
cout << "动物正在进食" << endl;
}
};
class Dog : public Animal
{
public:
void bark()
{
cout << "狗正在叫" << endl;
}
};
其中:
- Dog:派生类
- Animal:基类
- public:继承方式
- 冒号:继承语法中的分隔符
2. 派生类能够使用什么?
派生类可以使用基类中允许访问的成员,也可以定义自己的成员
class Animal
{
public:
string name;
void eat()
{
cout << "正在进食" << endl;
}
};
class Dog : public Animal
{
public:
int age;
void bark()
{
cout << "正在叫" << endl;
}
};
int main()
{
Dog d;
d.name = "小狗"; //基类成员
d.age = 2; //派生类成员
d.eat(); //基类成员函数
d.bark(); //派生类成员函数
return 0;
}
这里需要理解:
继承不是简单地将基类代码复制粘贴到派生类中
从对象模型来看,派生类对象包含一个基类子对象,同时还可以包含派生类自身新增的成员
例如:
Dog d;
可以将它理解为:
Dog 对象
│
├── Animal 基类子对象
│ └── name
│
└── Dog 自身部分
└── age
注意:成员函数的代码通常由同一个类的对象共享,并不是每个对象都存储一份成员函数代码
三、继承中的访问权限
这是继承中非常重要的知识点,也是面试高频考点
C++ 类成员主要有三种访问权限:
- public:公有成员,可以在允许的范围内被外部访问
- protected:保护成员,可以被本类成员以及符合访问规则的派生类成员访问,普通外部代码不能直接访问
- private:私有成员,只能由本类成员函数、友元等符合访问规则的代码访问,派生类不能直接访问
1. 基类中的三种访问权限
class Animal
{
public:
int a = 10;
protected:
int b = 20;
private:
int c = 30;
};
class Dog : public Animal
{
public:
void show()
{
cout << a << endl; //正确:public
cout << b << endl; //正确:protected
//cout << c << endl; //错误:private
}
};
int main()
{
Dog d;
cout << d.a << endl; //正确
//cout << d.b << endl; //错误
//cout << d.c << endl; //错误
d.show();
return 0;
}
为什么 a 和 b 可以访问,而 c 不可以?
因为:
- public 成员允许派生类访问
- protected 成员允许派生类访问
- private 成员不允许派生类直接访问
这里需要特别注意:
基类的 private 成员仍然存在于派生类对象中的基类子对象中,但派生类不能直接访问它
例如:
class Animal
{
private:
int weight = 50;
};
class Dog : public Animal
{
public:
void show()
{
//cout << weight; //错误
}
};
虽然 Dog 对象包含 Animal 基类子对象,而 weight 仍然属于这个基类子对象,但 weight 的私有访问权限并没有因为继承而改变
因此需要区分两个问题:
- 成员是否存在:属于对象组成的问题
- 成员能否访问:属于访问权限的问题
两者不能混为一谈
2. 如何间接访问基类的 private 成员?
可以通过基类提供的 public 或 protected 成员函数间接访问
#include<iostream>
using namespace std;
class Animal
{
private:
int weight = 50;
public:
int getWeight()
{
return weight;
}
void setWeight(int w)
{
weight = w;
}
};
class Dog : public Animal
{
public:
void show()
{
cout << getWeight() << endl;
}
};
int main()
{
Dog d;
d.show();//50
d.setWeight(60);
cout << d.getWeight() << endl; //60
return 0;
}
执行过程:
这也是面向对象封装思想的体现:将数据隐藏起来,通过接口控制数据的访问和修改
四、三种继承方式
C++ 提供三种继承方式:
- public:公有继承
- protected:保护继承
- private:私有继承
它们决定基类中的 public 和 protected 成员在派生类中的访问级别
1. 三种继承方式的区别
| public | public | protected | private |
| protected | protected | protected | private |
| private | 不可直接访问 | 不可直接访问 | 不可直接访问 |
注意:基类 private 成员仍然存在于基类子对象中,只是派生类不能直接访问
2. 代码理解
class Animal
{
public:
void eat() {}
protected:
void sleep() {}
private:
void breathe() {}
};
class Dog : public Animal {};
class Cat : protected Animal {};
class Tiger : private Animal {};
对于 Dog:
- eat() 仍然是 public
- sleep() 仍然是 protected
- breathe() 不能直接访问
对于 Cat:
- eat() 在 Cat 中变成 protected
- sleep() 仍然是 protected
对于 Tiger:
- eat() 在 Tiger 中变成 private
- sleep() 在 Tiger 中变成 private
注意:三种继承方式都不能让派生类直接访问基类的 private 成员
3. 默认继承方式
class Animal {};
class Dog : Animal {}; //默认 private 继承
struct Cat : Animal {}; //默认 public 继承
需要记住:
- class 声明的派生类,默认采用 private 继承
- struct 声明的派生类,默认采用 public 继承
实际开发中建议显式写出继承方式,避免歧义
一般情况下,使用 public 继承来表达合理的 is-a(是一种)关系
例如:Dog 是一种 Animal
class Dog : public Animal {};
五、派生类的构造函数和析构函数
理解了继承之后,还需要解决一个问题:
创建派生类对象时,基类部分是如何初始化的?
答案是:
构造时先构造基类,再构造派生类;析构时先析构派生类,再析构基类
1. 构造函数和析构函数的执行顺序
#include<iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal 构造函数" << endl;
}
~Animal()
{
cout << "Animal 析构函数" << endl;
}
};
class Dog : public Animal
{
public:
Dog()
{
cout << "Dog 构造函数" << endl;
}
~Dog()
{
cout << "Dog 析构函数" << endl;
}
};
int main()
{
Dog d;
return 0;
}
运行结果:
Animal 构造函数
Dog 构造函数
Dog 析构函数
Animal 析构函数
为什么是这个顺序?
因为派生类对象包含基类子对象
创建 Dog 对象时,需要先初始化 Animal 基类子对象,再初始化 Dog 自身部分
销毁时则相反,先销毁 Dog 自身部分,再销毁 Animal 基类子对象
可以记忆为:
构造:基类 → 派生类
析构:派生类 → 基类
2. 基类有带参数的构造函数怎么办?
例如:
class Animal
{
public:
Animal(string name)
{
cout << "动物名字:" << name << endl;
}
};
class Dog : public Animal
{
public:
Dog(string name) : Animal(name)
{
cout << "Dog 构造函数" << endl;
}
};
重点在于:
Dog(string name) : Animal(name)
这里的 Animal(name) 是成员初始化列表中对基类构造函数的调用,用于初始化派生类对象中的基类子对象
如果基类没有可用的默认构造函数,而派生类又没有显式调用合适的基类构造函数,就会出现编译错误
六、面试考点
1. 什么是继承?有什么作用?
继承是 C++ 面向对象编程的重要机制,允许派生类以基类为基础获得其可继承的成员,并在此基础上扩展新的属性和行为
主要作用是代码复用、扩展类的功能以及建立类之间的层次关系
2. 继承和派生有什么区别?
继承和派生描述的是同一种类关系,只是角度不同
继承强调派生类获得基类的可继承成员,派生强调以已有类为基础定义新的类
3. 基类的 private 成员能否被派生类访问?
不能直接访问
基类的 private 成员仍然存在于派生类对象中的基类子对象中,但由于访问权限限制,派生类不能直接访问
可以通过基类提供的 public 或 protected 成员函数间接访问
4. public、protected、private 三种继承有什么区别?
它们主要决定基类的 public 和 protected 成员在派生类中的访问级别
public 继承:public 仍然是 public,protected 仍然是 protected
protected 继承:public 和 protected 都变为 protected
private 继承:public 和 protected 都变为 private
三种继承方式都不能让派生类直接访问基类的 private 成员
5. 派生类对象创建和销毁的顺序是什么?
构造时先构造基类,再构造派生类
析构时先析构派生类,再析构基类
原因是派生类对象包含基类子对象,需要先初始化基类部分
6. 派生类可以继承基类的构造函数和析构函数吗?
构造函数和析构函数不能像普通成员函数一样被继承
派生类对象构造时会调用基类构造函数,销毁时会调用基类析构函数
C++11 还允许使用 using Base::Base; 声明继承构造函数,但这并不意味着派生类继承了基类的析构函数
七、总结
C++ 继承是面向对象编程中的重要机制,其核心思想是通过已有类构建新的类,实现代码复用和功能扩展
上面笔记内容简要概况为以下内容:
核心记忆:继承解决的是类之间的代码复用和层次关系问题,访问权限决定成员能否被访问
网硕互联帮助中心






评论前必须登录!
注册