深入理解C++ || NO.2 类(class)
前言
在 C++ 编程语言的学习体系中,类(class)是面向对象编程思想的入门基石,也是构建复杂程序逻辑的核心载体。不同于 C 语言仅将结构体作为单纯数据集合的用法,C++ 引入了类的概念,将数据属性与行为方法有机结合,依托访问权限控制实现基础封装特性。
类与结构体既有语法上的相通之处,也存在默认访问权限等关键细节差异,是初学者极易混淆的知识点。本文将从基础概念出发,通俗易懂地讲解 C++ 类与结构体的定义规则、成员构成、访问权限、对象使用及基础构造析构相关内容,帮助学习者理清二者的联系与区别,夯实 C++ 面向对象编程的入门基础。
1. C++的类(class)到底是什么?
1.1 类的本质
类 = 面向对象的核心 = 封装「属性」+「行为」的模板
- 属性:描述事物的特征(变量),理解为结构体的主要存放对象.
- 行为:描述事物能做什么(函数),结构体中一般不使用函数.
类本身不是真实数据,用类创建出来的变量叫「对象」.
形象的比喻: 类 == 图纸 对象 == 按图纸盖出来的房子
1.2 类的语法结构
class 类名
{
访问权限:
成员变量(属性)
成员函数(行为)
};
除了访问权限,类的其它语法部分都与结构体相同.
1.3 类的三大访问权限 (必须掌握)
(1). public 公有
本类内、类外都能访问。
(2). private 私有
只有类自己内部能访问,外部不能直接读写。→ 这就是封装:保护数据不被乱改。
这里唯一一个例外叫友元(friend) : friend 可以打破 private 权限,强行访问私有成员
一句话:friend 就是给外人开特权,让外人能访问我类里的 private 私有成员
这部分先说到这里,有个印象就好,之后详细解释.
(3). protected 保护
类内部 + 子类可以访问,外部不能访问。
1.4 类的基本写法示例
#include <iostream>
#include <string>
using namespace std;
class Student {
private:
// 私有成员变量(外部不能直接改)
string name;
int age;
public:
// 设置信息
void setInfo(string n, int a) {
name = n;
age = a;
}
// 显示信息
void showInfo() {
cout << "姓名:" << name << endl;
cout << "年龄:" << age << endl;
}
};
int main() {
// 创建对象
Student s;
// 调用公有成员函数
s.setInfo("张三", 18);
s.showInfo();
return 0;
}
1.5. 类的四大特殊函数(默认自带)
即使你不写,编译器也会自动生成:(后面出一期详细讲解)
(1). 构造函数:创建对象时自动调用
(2). 析构函数:销毁对象时自动调用
(3). 拷贝构造函数:用对象初始化新对象
(4). 赋值运算符重载:对象之间赋值
2. C++ 类(class)与结构体(struct)的区别(超详细)
一句话总结:
C++ 中 class 和 struct 99% 完全一样,唯一区别是:默认访问权限不同。
2.1 详细对照表
| 特性 | class(类) | struct(结构体) |
|---|---|---|
| 默认访问权限 | private(私有) | public(公开) |
| 默认继承权限 | private 继承 | public 继承 |
| 能否有成员函数 | 可以 | 可以 |
| 能否有构造 / 析构 | 可以 | 可以 |
| 能否继承 | 可以 | 可以 |
| 能否多态 | 可以 | 可以 |
| 习惯用途 | 面向对象封装 | 纯数据结构 |
2.2 代码对比
struct 默认 public
struct Student {
int id; // 默认 public
string name;
};
int main() {
Student s;
s.id = 1; // 可以直接访问
return 0;
}
class 默认 private
class Student {
int id; // 默认 private
string name;
};
int main() {
Student s;
// s.id = 1; ❌ 报错,不能访问
return 0;
}
3. 重要结论
(1). C 语言的 struct 只能存变量,不能有函数。
(2).C++ 的 struct 完全是 class 的翻版,功能几乎相同,只是默认访问权限不同。
(3). 类和结构体都有:成员变量、成员函数、三种访问权限、构造析构函数
(4). 实际开发习惯:类适合封装隐藏数据,结构体适合简单做数据集合。
- struct:存简单数据(坐标、颜色、顶点)
- class:封装复杂功能(日志、数据库、网络、算法)
4. 实战: 用类写一个专业级基础日志类(Logger)
首先,我们这里要写的日志功能是:给程序打印 “带时间、带等级、可过滤” 的提示信息,方便程序员看程序运行状态、找错误.
4.1 使用枚举
这里需要表示日志等级,因为INFO, WARN, ERROR等都是同一类型的数据,所以我们使用枚举用来表示【日志等级】,让代码更清晰、更安全、更好维护。
enum class Level
{
INFO = 0,
WARN = 1,
ERROR = 2
};
接下来要创建一个private变量来存放要设置的"我的日志等级",当信息等级大于等于个人设置的日志等级时,信息才会被打印.
4.2 m_loglevel
这里顺便初始化为INFO(最低的等级).
private:
Level m_logLevel = INFO;
4.3 时间戳
在下面来到最有趣的一个部分:时间戳.
// 获取当前时间字符串
string getTime() const
{
time_t now = time(nullptr);
char buf[20];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&now));
return string(buf);
}
超详细逐行讲解:
(1). time_t now = time(nullptr);
作用:获取从 1970 年 1 月 1 日 到现在一共过了多少秒这叫 Unix 时间戳。
例子:现在是 1735976652 秒计算机内部就是用这个数字表示时间。
(2). 2. char buf[20];
作用:开一个字符数组,用来存我们要的格式:
2025-12-19 15:30:45
数一数长度为19 个字符,所以字符数组大小开 20 足够。
(3).strftime(...) 最重要部分!
作用:把数字时间戳 → 人类能看懂的时间字符串
使用语法:
strftime(
存结果的数组,
数组大小,
格式字符串,
时间结构体
);
格式符号解释:
%Y 年 (2025)
%m 月 (12)
%d 日 (19)
%H 时 (15)
%M 分 (30)
%S 秒 (45)
所以数据从这样:
"%Y-%m-%d %H:%M:%S"
变成这样:
2025-12-19 15:30:45
(4). localtime(&now)
作用:把系统时间秒数→ 转成 年、月、日、时、分、秒 的结构体
(5).return string(buf);
最终输出结果:
[2025-12-19 15:30:45] [INFO] 程序启动成功
把字符数组转成字符串,返回给日志输出使用。
4.4 通用打印函数
// 通用打印函数
void print(Level level, const string& levelStr, const char* message)
{
if (level >= m_logLevel)
{
cout << "[" << getTime() << "] [" << levelStr << "] " << message << endl;
}
}
如果在类中写三个不同的打印函数不免显得累赘复杂(我之前就是这么写的haha),所以这里定义一个通用打印函数来满足所有需求.
第一个传入的参数是当前这条日志自己的等级,可以看到下面会与设置的日志等级比较,如果大于等于等级,就会打印信息.
传入的第二个参数是用来在日志里显示 [INFO] / [WARN] / [ERROR] 这些文字标签的.看到输出标签中间的[INFO][WARN][ERROR]就是 第二个参数 levelStr 输出的!
传入的第三个参数是自己输入要显示的信息,在下面接着显示.
4.5 日志等级设置
public:
void setLogLevel(Level level)
{
m_logLevel = level;
}
这个函数的功能就是传入一个等级,将传入的等级赋给m_loglevel,也就是设置了日志等级.
4.6 三个等级函数
void info(const char* message)
{
print(Level::INFO, "INFO", message);
}
void warn(const char* message)
{
print(Level::WARN, "WARN", message);
}
void error(const char* message)
{
print(Level::ERROR, "ERROR", message);
}
4.7 主函数main
int main(void)
{
Log log;
// 设置日志等级为 WARN,只会打印 WARN 和 ERROR
log.setLogLevel(Log::Level::WARN);
log.info("程序启动成功"); // 等级0 < 1,不打印
log.warn("内存不足"); // 等级1 >= 1,打印
log.error("文件打开失败"); // 等级2 >= 1,打印
return 0;
}
运行结果:
[2026-05-07 20:11:18] [INFO] 程序启动
[2026-05-07 20:11:18] [WARN] 内存不足
[2026-05-07 20:11:18] [ERROR] 文件打开失败
下面附上完整代码:
#include <iostream>
#include <ctime>
#include <string>
using namespace std;
class Log
{
public:
// 日志等级:数字越大,优先级越高
enum class Level
{
INFO = 0,
WARN = 1,
ERROR = 2
};
private:
Level m_logLevel = Level::INFO;
// 获取当前时间字符串
string getTime() const
{
time_t now = time(nullptr);
char buf[20];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&now));
return std::string(buf);
}
// 通用打印函数
void print(Level level, const std::string& levelStr, const char* message)
{
if (level >= m_logLevel)
{
std::cout << "[" << getTime() << "] [" << levelStr << "] " << message << std::endl;
}
}
public:
void setLogLevel(Level level)
{
m_logLevel = level;
}
void info(const char* message)
{
print(Level::INFO, "INFO", message);
}
void warn(const char* message)
{
print(Level::WARN, "WARN", message);
}
void error(const char* message)
{
print(Level::ERROR, "ERROR", message);
}
};
int main(void)
{
Log log;
// 设置日志等级为 INFO,会打印INFO, WARN 和 ERROR
log.setLogLevel(Log::Level::INFO);
log.info("程序启动");
log.warn("内存不足");
log.error("文件打开失败");
return 0;
}
在此不胜感谢能够看到这里的同学,如果发现文章有错误还请不吝赐教.
后续内容会不断更新,感兴趣的同学可以关注点赞收藏慢慢观看.
万分感谢!
-------------------------------------------------------------------------------------------
完
2026.5.18
-----22.41
更多推荐



所有评论(0)