news 2026/2/6 11:07:17

C++面向对象与类和对象(一)----C++重要基础入门知识

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++面向对象与类和对象(一)----C++重要基础入门知识
hello,这里是AuroraWanderll。 兴趣方向:C++,算法,Linux系统,游戏客户端开发 欢迎关注,我将更新更多相关内容!

个人主页
这是类和对象系列的第一篇文章:
之前由于第一次发布时篇幅过长,可能导致阅读体验很差,现在我把他按要点进行了适当拆分,希望能帮助读者更好理解,也方便自己复习。

C++面向对象与类和对象(一)----C++重要基础入门知识

简易目录

  • 第一个重要问题:面向对象和面向过程的区别
  • 第二个重要问题:C++中结构体升级成了类
  • 第三个重要问题:类的定义与编码规范

第一个重要问题:面向对象和面向过程的区别

类和对象的知识是我们学习C++编程的基础,它在我们的知识体系中扮演了一个至关重要的角色。

但在开始学习类、对象、继承这些具体概念之前,我们首先要解决一个最根本的问题:到底什么是"面向对象"?它和我们可能更熟悉的"面向过程"编程有什么不同?

一、面向过程编程:关注步骤的"流水线"

核心思想:“怎么做?”——关注的是解决问题需要的一系列步骤或函数。

在面向过程的世界里,程序员像一个流水线工程师,他的任务是精确地设计出每一个操作步骤。对于"把大象装进冰箱"这个问题,他的思路会是这样的:

  1. 打开冰箱门。
  2. 把大象塞进去。
  3. 关上冰箱门。

面向过程的特点:

  • 核心是函数:程序由一个个函数组成,数据(如door(门的开关状态),content(冰箱中的内容))和操作数据的函数是分离的。
  • 线性思维:代码按照预定的步骤顺序执行。
  • 优点:流程直观,在解决小型、任务明确的问题时非常高效。

缺点:当系统变得庞大复杂时,数据和函数的关系会变得混乱。如果想增加一个"给冰箱通电"的功能,相关的数据和函数可能会散落在程序的各个角落,难以维护和扩展。

二、面向对象编程:关注对象之间的相互作用

核心思想:“谁来做?”——关注的是解决问题中涉及到的各类对象,以及它们之间的交互。

在面向对象的世界里,程序员像一个社会架构师。他首先会定义出这个问题的解决方案里有哪几种角色(),每种角色有什么属性(数据成员)和能力(成员函数)。对于同样的问题,他的思路会是:

  1. 这个问题里涉及几个对象?——冰箱大象
  2. 每个对象各自有什么属性和能力?
    • 冰箱:有门(状态)、内部容量、可以执行开门、装东西、关门等动作。
    • 大象:有名字、体积等属性。

面向对象的特点:

  • 核心是类和对象:程序是由多个对象组成的,对象是数据(属性)和操作(方法)的封装体。
  • 三大特性:
    • 封装:把数据和处理数据的方法捆绑在一起,并可以隐藏内部细节(利用public,protected,private等访问限定符,后面会讲)。比如,main函数不需要知道Refrigerator(初始化冰箱状态) 内部如何记录门的状态,只需要调用它的openDoor()方法。
    • 继承:允许我们基于已有的类创建新类,实现代码的复用和扩展。(后续章节详解)
    • 多态:允许不同的对象对同一消息做出不同的响应。(后续章节详解)
  • 优点:代码结构清晰,更接近现实世界,易于维护、扩展和复用。当需求变化时,通常只需要修改或扩展某个类,而不会影响整个程序,方便工程管理。
  1. 举个例子:我们在竞技游戏中,某个角色的技能出现了bug,例如王者荣耀的露娜大招不刷新的bug。我们会说策划怎么还不修复露娜大招bug。而这个说法说的正是对象!我们的露娜是一个对象。如果我们是面向过程,我们则会说哪一步出了问题,而不是谁谁谁出了问题。

    可以看出,面向对象似乎比面向过程更贴近我们人的思维方式

第二个重要问题:C++中结构体升级成了类

在C语言中,我们学习的struct已经具有了很强大的功能,它能够在内部定义变量,我们用它来实现了很多很多数据结构。

而现在,在C++中,我们不在拘泥于定义变量,我们的对象,需要有更多的功能与方法,来进行更加复杂却便利的交互。

一、从C的struct到C++的class:一次伟大的升级

在C语言中,struct只是一个数据集合,它允许你将不同的数据类型组合在一起,形成一个新的复合类型。但它不能包含函数(方法)。

我们的struct中现在不仅可以定义变量,我们还可以定义包含方法,而我们所说的这些struct的功能已经无限接近class的功能。

C语言的struct(数据包):

c

// C语言代码 struct Person_C { char name[20]; int age; }; // 只能定义数据,不能定义函数 // 操作这个结构体的函数必须与结构体分离 void printPerson(struct Person_C p) { printf("Name: %s, Age: %d\n", p.name, p.age); }

到了C++,class的概念被引入,它极大地扩展了struct的能力。C++中的class是一个数据与行为的封装体

C++的class(智能对象):

// C++代码 class Person_CPP { private: std::string name; int age; public: // 可以在类内部定义函数(方法) void setName(const std::string& newName) { name = newName; } void setAge(int newAge) { if(newAge > 0) { // 可以加入逻辑验证 age = newAge; } } void print() const { // 成员函数 std::cout << "Name: " << name << ", Age: " << age << std::endl; } };

升级的核心点:

  1. 数据封装:可以包含成员变量(数据)和成员函数(行为)。
  2. 访问控制:引入了publicprivateprotected关键字来控制成员的访问权限。
  3. 构造函数/析构函数:提供了对象自动初始化和清理的能力。
  4. 继承与多态:成为面向对象继承体系的基础。

可以说我们的C++中的struct已经无限接近于class,但是他们还有一些细微的区别。

二、C++中的structclass:孪生兄弟的细微差别

现在来到问题的关键:C++中的structclass有什么区别?

答案是:除了默认的成员访问权限和默认的继承方式,它们没有任何功能上的区别。C++对原有的C风格struct进行了增强,让它拥有了class的所有能力。

核心区别:默认访问权限

  • class:成员和继承默认是private的。
  • struct:成员和继承默认是public的。

让我们用代码来展示这个区别:

示例1:默认成员访问权限

// 使用 class 关键字 class MyClass { int data; // 默认为 private,外部无法直接访问 public: void setData(int d) { data = d; } }; // 使用 struct 关键字 struct MyStruct { int data; // 默认为 public,外部可以直接访问 void setData(int d) { data = d; } }; int main() { MyClass obj_c; // obj_c.data = 10; // 错误!data 是 private 成员 obj_c.setData(10); // 必须通过公共接口 MyStruct obj_s; obj_s.data = 20; // 正确!data 是 public 成员 obj_s.setData(20); // 当然也可以这样做,也就是说通过公共接口或是直接访问都可以 return 0; }

示例2:默认继承方式

class Base { public: int x; }; // class 默认是 private 继承 class DerivedClass : Base { // 等价于 : private Base // 在DerivedClass内部,Base的public成员x变成了private }; // struct 默认是 public 继承 struct DerivedStruct : Base { // 等价于 : public Base // 在DerivedStruct内部,Base的public成员x仍然是public }; int main() { DerivedClass d_c; // d_c.x = 10; // 错误!由于是private继承,x在派生类中不可见 DerivedStruct d_s; d_s.x = 10; // 正确!由于是public继承,x在派生类中保持public return 0; }

重要提示:尽管默认行为不同,但我们强烈建议在代码中显式地写出访问控制符和继承方式,以避免混淆。

// 好的实践:显式声明 class MyClass { private: int data; public: MyClass() = default; }; struct MyStruct { public: // 虽然默认就是public,但写上更清晰 int data; MyStruct() = default; }; class Derived : public Base { // 显式声明public继承 // ... };
三、如何选择:structvsclass

既然功能几乎一样,我们该如何选择?C++社区形成了一些约定俗成的惯例

  • 使用struct
    • 当你主要需要一个纯粹的数据结构时。
    • 当所有成员都希望是公共的时(例如:坐标点Point {x, y},配置参数Config {width, height})。
    • 用于与C代码交互的兼容性结构体。
    • 在模板元编程中,有时用struct来作为"编译期函数"或特性标签,因为它默认的public更便捷。
  • 使用class
    • 当你需要定义一个具有复杂行为严格封装的抽象数据类型(Abstract Data Type)时。
    • 当你需要用到私有成员、保护成员,并需要通过公共接口来与对象交互时。
    • 当你打算使用继承和多态等面向对象特性时(尽管struct也可以,但class的语义更贴切)。

简单来说:struct感觉更像一个"数据包",而class感觉更像一个"功能完整的对象"。

总结
  • C++的class是C语言struct的超级增强版,引入了成员函数和访问控制。
  • 在C++中,structclass几乎完全相同的概念
  • 它们唯一的核心区别在于默认的访问权限和继承方式。
  • 在实际编程中,根据语义和惯例来选择使用哪一个,并始终显式地写出访问控制符,这能让你的代码更清晰、更专业。

理解了这一点,你就可以自信地在C++中使用这两种关键字了。

第三个重要问题:类的定义与编码规范

理解了面向对象的思想和classstruct的关系后,现在让我们正式学习如何在C++中定义类。这就像学习一门新语言的语法规则一样,是打好基础的关键一步。

一、类的基本语法结构
class ClassName { // 类体:由成员函数和成员变量组成 }; // 一定要注意这个分号!
  • class:定义类的关键字
  • ClassName:类的名字(遵循大驼峰命名法,如MyClass
  • {}:类的主体,包含类的所有成员(成员变量啊,成员函数啊等等)
  • ;:类定义结束的分号绝对不能省略!这是很多初学者容易犯错的地方。

类的成员分为两类:

  • 成员变量(属性):描述对象的特征,如人的年龄、姓名

  • 成员函数(方法):描述对象的行为,如人会走路、说话

    比如我们定义一个学生类,那么他的成员变量就是学号,姓名,年龄

    他的成员函数就是写作业,考试,上课

    就好像语文中的名词与动词的区别一样。

二、类的两种定义方式
方式1:声明和定义全部放在类体中
class Date { public: // 成员函数直接在类内部定义 void Init(int year, int month, int day) { _year = year; _month = month; _day = day; } void Print() { std::cout << _year << "-" << _month << "-" << _day << std::endl; } private: int _year; int _month; int _day; };

特点:

  • 优点:写法简单,适合教学和小型项目

  • 注意:在类内部定义的成员函数,编译器可能会将其当作内联函数处理

    这里我们可以简单了解一下内联函数(不是重点,可以跳过):

    传统函数调用过程:

    1. 跳转到函数地址
    2. 保存现场(压栈)
    3. 执行函数体
    4. 恢复现场(出栈)
    5. 跳转回调用处

    内联函数的处理方式:编译器会将内联函数的代码体直接展开到调用处,避免函数调用的开销(省略了压栈出栈一系列步骤)。

重要提醒:即使是显式使用inline关键字,也只是给编译器的建议,不是命令!编译器最终还是会根据自己的判断来决定是否真正内联。

内联的优缺点

优点:

  • 消除函数调用开销,提高性能
  • 避免跳转指令,有利于CPU指令流水线

缺点:

  • 代码膨胀:每个调用处都展开一份函数体

  • 可能增加编译后文件大小

  • 调试困难(函数调用栈信息可能丢失)

    可能被当成内联函数这一点是需要我们避免的,在这里编译器有完全的自主权,到底有没有被当成内联函数我们不得而知。这是一种不确定性,而且内联可能导致编译完之后代码膨胀(因为展开)

方式2:声明和定义分离(推荐!)

Date.h(头文件):

class Date { public: // 只在类中声明函数 void Init(int year, int month, int day); void Print(); private: int _year; int _month; int _day; };

Date.cpp(源文件):

#include "Date.h" #include <iostream> // 在源文件中定义函数,需要加上类名和作用域解析符 :: void Date::Init(int year, int month, int day) { _year = year; _month = month; _day = day; } void Date::Print() { std::cout << _year << "-" << _month << "-" << _day << std::endl; }

为什么推荐方式2?

  • 分离编译:头文件只放声明,源文件放实现,符合软件工程原则
  • 减少依赖:修改函数实现时,只需要重新编译对应的.cpp文件
  • 代码清晰:接口和实现分离,更易于阅读和维护
  • 避免内联膨胀:不会意外产生大量的内联函数(与上文相呼应)
三、成员变量命名规范(重要!)

这是一个看似简单但极其重要的实践问题。先看一个反面教材:

class Date { public: void Init(int year) { // 灾难!这里的year到底是成员变量还是函数参数? year = year; // 自己赋值给自己,毫无意义! } private: int year; // 成员变量 };

上面的代码中,year = year实际上只是把参数赋值给自己,成员变量完全没有被修改!

这样既导致了严重的南辕北辙的逻辑错误,也使阅读代码变成非常困难的一件事。比我们在C语言中的普通的变量命名规范重要的多,后者只是规范而前者是你必须好好命名否则会有严重后果

解决方案:使用命名区分

方案1:使用下划线前缀(常见于Linux/Unix风格)

class Date { public: void Init(int year) { _year = year; // 清晰明了! } private: int _year; // 成员变量加下划线 int _month; int _day; };

方案2:使用’m’前缀(常见于Windows/MFC风格)

class Date { public: void Init(int year) { mYear = year; // 同样清晰! } private: int mYear; // 成员变量加m前缀 int mMonth; int mDay; };
核心原则:
  • 一致性:选择一个风格并在整个项目中坚持使用
  • 可读性:让成员变量在代码中一眼就能被认出来
  • 避免歧义:确保不会与函数参数或局部变量混淆

在实际工作中,请遵循你所在团队的编码规范。没有绝对的对错,只有一致性最重要!

感谢你能够阅读到这里,如果本篇文章对你有帮助,欢迎点赞收藏支持,关注我, 我将更新更多有关C++,Linux系统·网络部分的知识。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/6 8:13:03

PAT 1135 Is It A Red-Black Tree

这一题的大意是给出一个二叉搜索树&#xff0c;让我们判断是不是红黑树&#xff0c; 只需要按题目要求构造好树&#xff0c;然后判断它是不是符合红黑树的条件即可。 题目给出了红黑树的性质&#xff0c;我们只需要判断它是否满足即可。 需要我们对二叉搜索树&#xff0c;建树&…

作者头像 李华
网站建设 2026/2/6 8:21:50

YOLOv8-Ultralytics 系列文章目录

YOLOv8-Ultralytics 系列文章目录 文章目录YOLOv8-Ultralytics 系列文章目录前言YOLOv8-Ultralytics 概述核心定位与优势核心技术架构YOLOv8-Ultralytics 源码讲解目标检测部分总结前言 YOLOv8是由Ultralytics公司&#xff08;创始人也是YOLO系列核心作者Joseph Redmon的合作者…

作者头像 李华
网站建设 2026/2/6 2:42:17

自动化运维工程师之ansible启动rpcbind和nfs服务

通过 systemd 模块分别启动 rpcbind 和 nfs 服务&#xff0c;并设置它们为开机自启&#xff0c;是 NFS 服务部署中启动相关服务的典型配置。下面我会逐部分解析代码的含义、作用以及关键细节。 一、代码整体功能总结 这段代码包含两个独立的 systemd 模块任务&#xff0c;依次完…

作者头像 李华
网站建设 2026/2/5 10:23:45

数字供应链系统哪个好?2025 供应链系统推荐排名来了,八大供应链系统

当数字化转型从“可选项”变为“必选项”&#xff0c;S2B2B供应链系统已成为企业重构供应链竞争力的核心工具。无论是解决传统批发企业“订单传递慢、库存不清”的沉疴&#xff0c;还是支撑新兴跨境商家“多渠道协同、全链路合规”的需求&#xff0c;一款高效的供应链系统都能让…

作者头像 李华
网站建设 2026/2/6 7:48:08

M.I.B.终极指南:解锁汽车娱乐系统的隐藏功能

你是否曾经对车载系统的功能限制感到困扰&#xff1f;为什么高端汽车的原厂娱乐系统总是缺少你想要的功能&#xff1f;如果你的车辆使用的是Harman MHI2或MHIG系列娱乐系统&#xff0c;那么M.I.B.就是你的完美解决方案。这个开源工具就像一个汽车系统的"多功能工具"&…

作者头像 李华