GESP 6 级 C++ 类相关知识点
2026-06-26 20:29:07
发布于:广东
GESP 6 级 C++ 类相关知识点详解
适用对象:准备 GESP C++ 六级的学生
学习目标:会看懂类相关代码,会写简单类,会理解封装、继承、多态的基本含义,并能在树、栈、队列等数据结构题中使用类组织代码。
目录
- 类是什么
- class 和 struct 的区别
- 访问权限:public、private、protected
- 成员变量与成员函数
- 对象的创建与访问
- 构造函数详解
- 析构函数详解
- this 指针
- 对象作为函数参数
- const 成员函数
- 封装
- 继承
- 多态
- 类与 STL 容器
- 类在树结构中的应用
- GESP 6 级常见易错点
- 推荐掌握程度
1. 类是什么
类可以理解为一种“自定义类型”。
以前我们会写:
int x;
double y;
string s;
这里的 int、double、string 都是类型。
而类可以让我们自己定义一种类型,例如:
class Student {
public:
string name;
int score;
};
这样 Student 就变成了一种新的类型。
使用时可以写:
Student a;
a.name = "Tom";
a.score = 90;
其中:
Student是类;a是对象;name和score是成员变量。
可以类比:
int x;
其中 int 是类型,x 是变量。
Student a;
其中 Student 是自定义类型,a 是这个类型的对象。
2. class 和 struct 的区别
在 C++ 中,class 和 struct 很相似,都可以包含变量和函数。
主要区别是:
| 写法 | 默认访问权限 |
|---|---|
struct |
public |
class |
private |
例如:
struct A {
int x;
};
class B {
int x;
};
使用:
A a;
a.x = 10; // 正确,因为 struct 默认 public
B b;
b.x = 10; // 错误,因为 class 默认 private
如果想让 class 中的成员可以在外部访问,需要加 public:。
class B {
public:
int x;
};
考试中经常考这一点:
class Node {
int val;
};
int main() {
Node x;
x.val = 5; // 编译错误
}
原因:class 中没有写 public:,所以 val 默认是私有成员。
3. 访问权限:public、private、protected
类中常见的访问权限有三种:
| 关键字 | 含义 |
|---|---|
public |
公有成员,类外部可以访问 |
private |
私有成员,只有类内部可以访问 |
protected |
保护成员,类内部和子类内部可以访问,类外部不能直接访问 |
最常见的是 public 和 private。
class Student {
private:
int score;
public:
void setScore(int x) {
score = x;
}
int getScore() {
return score;
}
};
使用:
Student s;
s.setScore(95);
cout << s.getScore() << endl;
但是不能这样写:
s.score = 95; // 错误,score 是 private
这样设计的好处是:可以保护数据,不让外部随意修改对象内部状态。
例如:
class Student {
private:
int score;
public:
void setScore(int x) {
if (0 <= x && x <= 100) {
score = x;
}
}
};
这样就可以避免外部设置出非法分数。
4. 成员变量与成员函数
类中的变量叫成员变量。
类中的函数叫成员函数。
class Rectangle {
private:
int width;
int height;
public:
void set(int w, int h) {
width = w;
height = h;
}
int area() {
return width * height;
}
};
使用:
Rectangle r;
r.set(3, 4);
cout << r.area() << endl; // 输出 12
其中:
width、height是成员变量;set、area是成员函数;r.set(3, 4)表示调用对象r的成员函数。
5. 对象的创建与访问
5.1 普通对象
class Point {
public:
int x, y;
};
int main() {
Point p;
p.x = 3;
p.y = 4;
cout << p.x << " " << p.y << endl;
}
普通对象使用 . 访问成员。
p.x
p.y
5.2 对象指针
Point p;
Point* q = &p;
q->x = 10;
q->y = 20;
对象指针使用 -> 访问成员。
q->x
q->y
q->x 等价于:
(*q).x
5.3 对象数组
Point a[100];
a[0].x = 1;
a[0].y = 2;
对象数组在树、图、模拟题中比较常用。
6. 构造函数详解
构造函数是类中非常重要的内容。
6.1 构造函数是什么
构造函数是在对象创建时自动调用的特殊函数,用来初始化对象。
构造函数有三个特点:
- 函数名必须和类名相同;
- 没有返回值,连
void都不能写; - 创建对象时会自动调用。
例如:
class Point {
public:
int x, y;
Point() {
x = 0;
y = 0;
}
};
使用:
Point p;
cout << p.x << " " << p.y << endl; // 0 0
当执行:
Point p;
时,程序会自动调用:
Point()
这就是构造函数。
6.2 默认构造函数
没有参数的构造函数叫默认构造函数,也叫无参构造函数。
class Student {
public:
string name;
int score;
Student() {
name = "unknown";
score = 0;
}
};
使用:
Student s;
cout << s.name << " " << s.score << endl;
如果一个类中没有写任何构造函数,编译器会自动生成一个默认构造函数。
例如:
class A {
public:
int x;
};
可以写:
A a;
但是要注意:如果成员变量是普通内置类型,例如 int,没有手动初始化时,它的值可能是不确定的。
6.3 带参数的构造函数
构造函数可以带参数。
class Point {
public:
int x, y;
Point(int a, int b) {
x = a;
y = b;
}
};
使用:
Point p(3, 4);
cout << p.x << " " << p.y << endl; // 3 4
含义是:创建 p 时,把 x 初始化为 3,把 y 初始化为 4。
6.4 构造函数重载
一个类可以有多个构造函数,只要参数列表不同。
class Point {
public:
int x, y;
Point() {
x = 0;
y = 0;
}
Point(int a, int b) {
x = a;
y = b;
}
};
使用:
Point p1; // 调用 Point()
Point p2(3, 4); // 调用 Point(int, int)
这叫构造函数重载。
6.5 带默认参数的构造函数
可以使用默认参数把多个构造函数合并。
class Point {
public:
int x, y;
Point(int a = 0, int b = 0) {
x = a;
y = b;
}
};
这样下面三种写法都可以:
Point p1; // x = 0, y = 0
Point p2(3); // x = 3, y = 0
Point p3(3, 4); // x = 3, y = 4
这种写法在算法竞赛中很常用。
6.6 初始化列表
构造函数还有一种更推荐的写法,叫初始化列表。
class Point {
public:
int x, y;
Point(int a = 0, int b = 0) : x(a), y(b) {
}
};
这一句:
Point(int a = 0, int b = 0) : x(a), y(b) {}
表示:
- 用
a初始化x; - 用
b初始化y。
等价于:
Point(int a = 0, int b = 0) {
x = a;
y = b;
}
对于 GESP 6 级来说,两种写法都可以掌握。
算法竞赛中常见写法:
struct Node {
int val, left, right;
Node(int v = 0, int l = -1, int r = -1) : val(v), left(l), right(r) {}
};
6.7 对象数组与默认构造函数
这一点很容易出错。
如果你写:
class Node {
public:
int val;
Node(int x) {
val = x;
}
};
然后写:
Node a[100];
这会编译错误。
原因是:创建对象数组时,需要给每个元素调用无参构造函数,但是这个类只有 Node(int x),没有 Node()。
正确写法 1:增加无参构造函数。
class Node {
public:
int val;
Node() {
val = 0;
}
Node(int x) {
val = x;
}
};
正确写法 2:使用默认参数。
class Node {
public:
int val;
Node(int x = 0) {
val = x;
}
};
这样就可以写:
Node a[100];
6.8 构造函数常见错误
错误 1:构造函数写了返回值
class A {
public:
void A() { // 错误
}
};
构造函数不能写返回值,正确写法是:
class A {
public:
A() {
}
};
错误 2:忘记默认构造函数
class A {
public:
A(int x) {}
};
A arr[10]; // 错误
因为 arr[10] 需要无参构造函数。
错误 3:构造函数名和类名不一致
class Student {
public:
Stu() { // 错误,这不是构造函数
}
};
构造函数名必须和类名完全相同。
7. 析构函数详解
析构函数和构造函数相反。
构造函数在对象创建时自动调用。
析构函数在对象销毁时自动调用。
7.1 析构函数是什么
析构函数是一种特殊成员函数,用来在对象销毁前执行收尾工作。
析构函数有几个特点:
- 函数名是
~类名; - 没有返回值;
- 没有参数;
- 一个类最多只能有一个析构函数;
- 对象销毁时自动调用。
例如:
class A {
public:
A() {
cout << "create" << endl;
}
~A() {
cout << "destroy" << endl;
}
};
使用:
int main() {
A x;
return 0;
}
输出大致是:
create
destroy
原因:
A x;创建对象,调用构造函数;main函数结束时,x被销毁,调用析构函数。
7.2 析构函数的写法
class 类名 {
public:
~类名() {
// 对象销毁前执行的代码
}
};
例如:
class Student {
public:
string name;
Student(string n = "unknown") {
name = n;
cout << name << " created" << endl;
}
~Student() {
cout << name << " destroyed" << endl;
}
};
使用:
int main() {
Student s("Tom");
cout << "main running" << endl;
return 0;
}
可能输出:
Tom created
main running
Tom destroyed
7.3 局部对象的析构时机
局部对象在离开作用域时被销毁。
class A {
public:
A() {
cout << "A created" << endl;
}
~A() {
cout << "A destroyed" << endl;
}
};
int main() {
{
A x;
cout << "inside block" << endl;
}
cout << "outside block" << endl;
}
输出:
A created
inside block
A destroyed
outside block
因为 x 是在 {} 里面创建的,出了这个代码块就会被销毁。
7.4 多个对象的析构顺序
局部对象通常按照“先构造的后析构,后构造的先析构”的顺序销毁。
class A {
public:
string name;
A(string n) {
name = n;
cout << name << " created" << endl;
}
~A() {
cout << name << " destroyed" << endl;
}
};
int main() {
A a("a");
A b("b");
A c("c");
return 0;
}
输出:
a created
b created
c created
c destroyed
b destroyed
a destroyed
记忆方式:像栈一样,先进后出。
7.5 析构函数常用来做什么
在普通 GESP 6 级题目中,析构函数一般不需要手写。
它主要用于释放资源,例如:
- 释放动态申请的内存;
- 关闭文件;
- 释放网络连接;
- 输出调试信息。
例如动态内存:
class Array {
private:
int* a;
public:
Array(int n) {
a = new int[n];
}
~Array() {
delete[] a;
}
};
这里:
a = new int[n];
表示申请一段动态数组。
delete[] a;
表示释放这段动态数组。
不过在 GESP 6 级和算法竞赛中,更推荐使用:
vector<int> a;
而不是手动 new 和 delete。
所以析构函数在六级阶段通常掌握概念即可。
7.6 析构函数常见错误
错误 1:析构函数写参数
class A {
public:
~A(int x) { // 错误
}
};
析构函数不能有参数。
错误 2:析构函数写返回值
class A {
public:
void ~A() { // 错误
}
};
析构函数不能写返回值。
错误 3:写多个析构函数
class A {
public:
~A() {}
~A(int x) {} // 错误
};
一个类最多只能有一个析构函数。
7.7 构造函数和析构函数对比
| 项目 | 构造函数 | 析构函数 |
|---|---|---|
| 调用时机 | 对象创建时 | 对象销毁时 |
| 函数名 | 和类名相同 | ~类名 |
| 返回值 | 没有返回值 | 没有返回值 |
| 参数 | 可以有参数 | 不能有参数 |
| 个数 | 可以有多个,构成重载 | 最多只能有一个 |
| 主要作用 | 初始化对象 | 清理对象资源 |
例子:
class A {
public:
A() {
cout << "constructor" << endl;
}
~A() {
cout << "destructor" << endl;
}
};
8. this 指针
在成员函数内部,this 表示当前对象的地址。
class Point {
public:
int x, y;
Point(int x, int y) {
this->x = x;
this->y = y;
}
};
这里参数也叫 x、y,成员变量也叫 x、y。
为了区分,需要写:
this->x = x;
左边的 this->x 是成员变量,右边的 x 是参数。
也可以避免重名:
class Point {
public:
int x, y;
Point(int a, int b) {
x = a;
y = b;
}
};
GESP 6 级只需要知道 this 表示“当前对象”即可。
9. 对象作为函数参数
对象可以作为函数参数。
class Point {
public:
int x, y;
Point(int x = 0, int y = 0) : x(x), y(y) {}
};
void print(Point p) {
cout << p.x << " " << p.y << endl;
}
使用:
Point a(3, 4);
print(a);
但是:
void print(Point p)
会复制一份对象。
如果对象比较大,推荐使用引用传参:
void print(const Point& p) {
cout << p.x << " " << p.y << endl;
}
其中:
const Point& p
含义是:
&表示引用传递,不复制对象;const表示函数内部不能修改这个对象。
算法竞赛中经常推荐这样写。
10. const 成员函数
如果一个成员函数不会修改对象,可以在函数后面加 const。
class Point {
private:
int x, y;
public:
Point(int x = 0, int y = 0) : x(x), y(y) {}
int getX() const {
return x;
}
};
这里:
int getX() const
表示 getX 不会修改对象内部数据。
常见搭配:
void print(const Point& p) {
cout << p.getX() << endl;
}
如果 getX() 后面不写 const,在某些情况下会报错,因为 p 是 const Point&,只能调用不会修改对象的成员函数。
11. 封装
封装是面向对象的核心思想之一。
封装的意思是:把数据和操作数据的函数放在一起,并隐藏内部细节。
例如手写一个简单栈:
class Stack {
private:
int a[1005];
int topIndex;
public:
Stack() {
topIndex = 0;
}
void push(int x) {
a[++topIndex] = x;
}
void pop() {
if (topIndex > 0) {
topIndex--;
}
}
int top() {
return a[topIndex];
}
bool empty() {
return topIndex == 0;
}
};
使用:
Stack st;
st.push(10);
st.push(20);
cout << st.top() << endl; // 20
st.pop();
cout << st.top() << endl; // 10
这里 a 和 topIndex 被放在 private 中,外部不能随便修改。
如果允许外部直接改:
st.topIndex = -100;
栈的状态就会被破坏。
所以封装的好处是:保护对象内部数据,让程序更安全、更清晰。
12. 继承
继承表示一个类可以复用另一个类的成员。
被继承的类叫父类,也叫基类。
继承出来的类叫子类,也叫派生类。
class Animal {
public:
void eat() {
cout << "eat" << endl;
}
};
class Dog : public Animal {
public:
void bark() {
cout << "wang" << endl;
}
};
使用:
Dog d;
d.eat(); // 从 Animal 继承来的
d.bark(); // Dog 自己的
这里:
class Dog : public Animal
表示 Dog 公开继承 Animal。
GESP 6 级通常只要求理解基本继承概念,不需要掌握复杂继承。
13. 多态
多态表示:同一个函数调用,在不同对象上有不同表现。
最典型的多态依赖 virtual 虚函数。
class Animal {
public:
virtual void speak() {
cout << "animal" << endl;
}
};
class Dog : public Animal {
public:
void speak() override {
cout << "wang" << endl;
}
};
class Cat : public Animal {
public:
void speak() override {
cout << "miao" << endl;
}
};
使用:
Dog d;
Cat c;
Animal* p;
p = &d;
p->speak(); // wang
p = &c;
p->speak(); // miao
关键点:
virtual void speak()
表示 speak 是虚函数。
当父类指针指向子类对象时,调用虚函数,会根据实际对象类型决定调用哪个版本。
GESP 6 级一般偏概念理解,不太会在编程题中强制要求复杂多态。
14. 类与 STL 容器
平时常用的 STL 容器,本质上也是类模板。
例如:
stack<int> st;
queue<int> q;
vector<int> v;
这些都是创建对象。
14.1 stack
stack<int> st;
st.push(1);
st.push(2);
cout << st.top() << endl; // 2
st.pop();
push、top、pop 都是成员函数。
14.2 queue
queue<int> q;
q.push(1);
q.push(2);
cout << q.front() << endl; // 1
q.pop();
14.3 priority_queue
优先队列在哈夫曼树中很常用。
小根堆写法:
priority_queue<int, vector<int>, greater<int>> pq;
哈夫曼合并例子:
while (pq.size() > 1) {
int a = pq.top(); pq.pop();
int b = pq.top(); pq.pop();
pq.push(a + b);
}
这也是“对象 + 成员函数”的典型使用。
15. 类在树结构中的应用
GESP 6 级会涉及树、二叉树、二叉排序树等内容,类可以用来表示节点。
15.1 指针版树节点
class TreeNode {
public:
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int v = 0) {
val = v;
left = nullptr;
right = nullptr;
}
};
创建节点:
TreeNode* root = new TreeNode(5);
root->left = new TreeNode(3);
root->right = new TreeNode(7);
访问:
cout << root->val << endl;
因为 root 是指针,所以访问成员要用 ->。
15.2 数组模拟树节点
算法竞赛中更推荐数组模拟,避免频繁 new。
class Node {
public:
int val;
int left;
int right;
Node(int v = 0, int l = -1, int r = -1) {
val = v;
left = l;
right = r;
}
};
Node tree[1005];
其中:
val表示节点权值;left表示左儿子编号;right表示右儿子编号;-1表示没有儿子。
这种写法更适合 GESP、CSP-J、算法竞赛训练。
16. GESP 6 级常见易错点
16.1 类定义后面必须有分号
class A {
}; // 这里必须有分号
16.2 class 默认 private
class A {
int x;
};
int main() {
A a;
a.x = 1; // 错误
}
16.3 构造函数不能写返回值
class A {
public:
A() {} // 正确
void A() {} // 错误
};
16.4 析构函数不能有参数
class A {
public:
~A() {} // 正确
~A(int x) {} // 错误
};
16.5 对象用点,指针用箭头
A a;
a.f();
A* p = &a;
p->f();
16.6 对象数组需要默认构造函数
class A {
public:
A(int x) {}
};
A arr[10]; // 错误
改成:
class A {
public:
A(int x = 0) {}
};
16.7 private 成员不能在类外直接访问
class A {
private:
int x;
public:
void setX(int v) {
x = v;
}
};
int main() {
A a;
a.x = 10; // 错误
a.setX(10); // 正确
}
17. 推荐掌握程度
GESP 6 级阶段,类相关知识建议掌握到以下程度:
- 会定义简单类;
- 会区分
class和struct的默认访问权限; - 会使用
public、private、protected; - 会写成员变量和成员函数;
- 会创建对象,并使用
.或->访问成员; - 会写默认构造函数、带参数构造函数、带默认参数构造函数;
- 知道构造函数会在对象创建时自动调用;
- 知道析构函数会在对象销毁时自动调用;
- 理解封装、继承、多态的基本含义;
- 能用类表示树节点、栈、队列等简单结构。
不需要在六级阶段深入掌握:
- 多重继承;
- 复杂虚函数表;
- 运算符重载;
- 模板类设计;
- 智能指针;
- 友元函数;
- 抽象类体系;
- 复杂内存管理。
学习重点应该是:
类语法会用,面向对象概念会判断,编程题仍以树、搜索、动态规划、栈、队列为主。
练习题
题 1:判断是否能编译
class A {
int x;
};
int main() {
A a;
a.x = 10;
return 0;
}
答案:不能编译。
原因:class 默认成员是 private,类外不能访问 x。
题 2:输出什么
#include <bits/stdc++.h>
using namespace std;
class A {
public:
A() {
cout << "C";
}
~A() {
cout << "D";
}
};
int main() {
A a;
cout << "M";
return 0;
}
答案:
CMD
解释:
- 创建对象
a,调用构造函数,输出C; - 执行
cout << "M",输出M; main结束,对象a销毁,调用析构函数,输出D。
题 3:对象数组为什么报错
class Node {
public:
int val;
Node(int x) {
val = x;
}
};
Node a[100];
答案:因为 Node a[100] 需要调用无参构造函数,但 Node 类中只有 Node(int x)。
修改:
class Node {
public:
int val;
Node(int x = 0) {
val = x;
}
};
题 4:补全一个树节点类
要求:定义一个 Node 类,包含 val、left、right 三个成员,默认值分别为 0、-1、-1。
参考答案:
class Node {
public:
int val;
int left;
int right;
Node(int v = 0, int l = -1, int r = -1) {
val = v;
left = l;
right = r;
}
};
也可以写成初始化列表:
class Node {
public:
int val;
int left;
int right;
Node(int v = 0, int l = -1, int r = -1) : val(v), left(l), right(r) {}
};
这里空空如也





















有帮助,赞一个