ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

C++基础笔记(1):面向对象:类,对象,成员变量

2026/10/5 20:55:37 拓冰建站 浏览量
C++基础笔记(1):面向对象:类,对象,成员变量 本笔记用于记录作者对C的一些学习心得学习资料是基于夏曹俊老师的工程C基础入门课程欢迎大佬们指点和交流谢谢1. 概述1.1. 面向对象的定义与特点1定义一句话总结是一种以“对象”为中心的程序组织方式。接下来我们展开讲一下。面向对象编程Object-Oriented ProgrammingOOP是一种将程序结构建立在“对象”之上的编程思想。在这种范式中软件系统不再被简单地看作一系列函数的执行过程而是由一组相互协作的对象组成。每个对象同时包含数据状态和操作这些数据的方法行为从而形成一个相对独立、可复用的软件单元。在 C 中对象通常由类class定义。类可以理解为一种用户自定义类型它描述了一类事物共有的属性和行为而对象则是该类型的具体实例。通过这种方式程序能够更自然地模拟现实世界中的实体例如“玩家”“设备”“窗口”等从而使复杂系统的结构更加清晰、可理解。2特点1. 面向对象并不是唯一的开发方式会与其他范式(⾯向过程/函数式/描述式)混合使⽤在实际工程中它通常不会单独使用而是与其他编程思想结合。例如性能关键部分可能仍采用面向过程的写法数据处理场景可能使用函数式风格而泛型编程如 C STL则提供高度复用的算法与容器。面向对象主要用于描述系统结构和模块关系而底层实现可以根据需求选择更高效或更灵活的方式。因此OOP 的关键并不是盲目的“把所有代码都写成类”而是知道在什么情况下使用对象模型能够提升可维护性和扩展性。2. 类与对象构成面向对象程序的基本单元在 C 的面向对象体系中最核心的概念是类Class和对象Object。类用于定义一种新的数据类型它规定了对象应具有的数据成员和成员函数对象则是根据该类型创建的具体实体每个对象拥有独立的内部状态。可以将类理解为“设计图”或“模板”而对象是根据这份设计制造出来的实际产品。同一个类可以创建多个对象它们共享相同的结构但数据互不影响。例如多个角色对象都具有生命值属性但各自的生命值可以不同。这种“结构共享、状态独立”的特性使得面向对象非常适合描述现实系统中的多个实体。2. 类和对象2.1. 类1类是什么一句话 用户自定义的类型(类比int/float等内置类型)在 C 中类class本质上是一种用户自定义类型用于描述一类对象的共同结构和行为。它的地位类似于语言内置类型如 int、float、string但由程序员根据需求自行设计。通过类可以将复杂的数据结构和相关操作封装在一起使程序能够以更高层次的抽象来表达问题。与基本类型不同类不仅可以包含数据还可以定义操作这些数据的函数从而形成“数据 行为”的统一体。例如可以定义一个表示视频播放器的类其中包含播放状态、音量、文件路径等属性以及播放、暂停、停止等操作。这样程序不再直接处理零散变量而是操作具有明确语义的对象。2组成元素一个完整的类通常包含两类核心组成部分数据成员和成员函数。1. 数据成员用于描述对象的状态信息例如一个角色的生命值、位置或名称2. 成员函数则定义对象能够执行的行为例如移动、攻击或显示状态。二者共同构成对象的完整功能。其实这种结构正是体现了面向对象的封装思想数据和操作数据的方法被组织在同一个实体中而不是分散在程序的各个位置。通过这种方式可以避免外部代码随意修改对象内部状态从而提高程序的可靠性和可维护性。在实际工程中通常还会通过访问控制如 private、public进一步限制外部访问权限使类成为稳定的模块边界。3设计目标一句说人话总结构建可复用、可维护的程序模块。接下来展开说或者可以说我啰嗦设计类的根本目的是构建具有明确职责的软件组件。一个良好的类应当同时具备数据和行为能够独立完成某一类功能。如果一个结构只包含数据而没有相关操作通常更适合使用结构体或普通数据类型如果只包含函数而不依赖内部状态则可能更适合设计为普通函数或工具类。如果有C语言基础的同学可以类比一下其实class 与 struct 在语法上非常相似主要区别在于默认访问权限不同class 的成员默认是 private而 struct 默认是 public。2.2.对象1对象是类的具体实例对象Object是根据类Class创建出来的具体实体可以理解为类的“实例”或“变量”。如果类是一份设计图或模板那么对象就是按照该模板制造出的实际产品。在程序运行时只有对象才真正占用内存并参与计算而类本身只是对结构和行为的描述。在 C 中通过定义变量的方式即可创建对象。例如定义一个表示角色的类后可以创建多个角色对象每个对象都具备相同的属性结构和成员函数但它们在内存中是独立存在的。#include iostream #include string class Character { public: std::string name; int health; void showStatus() const { std::cout name | Health: health std::endl; } }; int main() { // 根据同一个类创建多个对象 Character player1; player1.name Knight; player1.health 100; Character player2; player2.name Archer; player2.health 80; // 每个对象拥有独立状态 player1.showStatus(); player2.showStatus(); return 0; }在 C 中可以通过定义变量的方式创建对象。下面的示例定义了一个表示角色的类并创建了两个不同的角色对象。虽然它们来自同一个类但各自拥有独立的属性值。程序运行后可以看到每个对象的生命值和名称互不影响这说明对象在内存中是独立存在的。类提供了统一的结构和行为而对象则保存具体的数据状态。2同一类可以创建多个对象且状态相互独立一个类通常可以生成任意数量的对象这些对象共享相同的类型定义但各自拥有独立的数据状态。例如多个玩家对象都具有生命值和名称属性但每个玩家的生命值变化互不影响。这种“结构共享、状态独立”的特性是面向对象建模现实世界的重要基础。由于每个对象在内存中都有独立存储空间对某个对象成员变量的修改不会影响其他对象。这一点使得程序能够同时管理大量具有相同结构但状态不同的实体例如游戏中的多个角色、系统中的多个设备实例或界面中的多个窗口。#include iostream #include string class Character { public: std::string name; int health; void showStatus() const { std::cout name | Health: health std::endl; } }; int main() { // 根据同一个类创建多个对象 Character player1; player1.name Saber; player1.health 100; Character player2; player2.name Archer; player2.health 80; // 每个对象拥有独立状态 player1.showStatus(); player2.showStatus(); return 0; }在 C 中可以通过定义变量的方式创建对象。下面的示例定义了一个表示角色的类并创建了两个不同的角色对象。虽然它们来自同一个类但各自拥有独立的属性值。程序运行后可以看到每个对象的生命值和名称互不影响这说明对象在内存中是独立存在的。类提供了统一的结构和行为而对象则保存具体的数据状态。3对象是程序运行时的基本操作单位在面向对象程序中实际参与运行的是对象而不是类。程序通过对象调用成员函数、读取或修改成员变量从而实现功能逻辑。因此可以将对象看作系统中的“活动实体”而类则是定义这些实体行为规则的蓝图。从类型系统的角度来看类相当于一种自定义类型而对象相当于该类型的变量。这种关系类似于 int 与具体整数变量之间的关系只不过类可以描述更加复杂的数据结构和行为。总结一句话对象是面向对象程序中真正存在和运作的实体类通过定义对象的结构和行为使复杂系统能够以多个独立实例的形式进行组织和管理。3. 代码实现我们基于上面的知识写了一个有趣的模拟对战小代码可以更直观的展示类和对象的概念#include iostream #include string #include cstdlib #include ctime // 时间库用于 time() 生成随机种子 // Character 类描述“游戏角色”这一类对象的共同属性和行为 class Character { public: std::string name; // 角色名称 int health 100; // 角色生命值默认 100 int minAttack 10; // 最小攻击力 int maxAttack 20; // 最大攻击力 // 显示当前角色状态 void showStatus() const { std::cout name | Health: health | Attack Range: minAttack - maxAttack std::endl; } // 判断角色是否存活 bool isAlive() const { return health 0; } // 生成一次随机伤害并附带 miss / critical 判定 int generateDamage(bool isMiss, bool isCritical) const { // 先默认本次攻击不是 miss也不是暴击 isMiss false; isCritical false; // 生成 0~99 的随机数用于判定事件概率 int eventRoll rand() % 100; // 15% 概率 miss伤害直接为 0 if (eventRoll 15) { isMiss true; return 0; } // 在 [minAttack, maxAttack] 区间内生成普通伤害 int damage minAttack rand() % (maxAttack - minAttack 1); // 20% 概率暴击双倍伤害 if (eventRoll 15 eventRoll 35) { isCritical true; damage * 2; } return damage; } // 当前对象攻击目标对象 void attackTarget(Character target) { bool isMiss false; // 是否 miss bool isCritical false; // 是否暴击 // 调用成员函数生成本次攻击伤害 int damage generateDamage(isMiss, isCritical); // 根据攻击结果输出不同提示 if (isMiss) { std::cout name attacks target.name but misses! std::endl; } else if (isCritical) { std::cout name attacks target.name with a CRITICAL HIT for damage damage! std::endl; } else { std::cout name attacks target.name for damage damage. std::endl; } // 扣减目标生命值 target.health - damage; // 防止生命值小于 0 if (target.health 0) { target.health 0; } } }; // 主函数程序入口 int main() { // 使用当前时间作为随机种子 // 这样每次运行程序时随机结果都不一样 srand(static_castunsigned int(time(nullptr))); // 创建第一个角色对象 Character player1; player1.name Saber; player1.health 120; player1.minAttack 10; player1.maxAttack 20; // 创建第二个角色对象 Character player2; player2.name Archer; player2.health 80; player2.minAttack 12; player2.maxAttack 24; // 输出初始状态 std::cout Initial Status std::endl; player1.showStatus(); player2.showStatus(); std::cout \n Battle Start std::endl; int round 1; // 记录当前回合数 // 只要双方都活着就持续战斗 while (player1.isAlive() player2.isAlive()) { std::cout \n--- Round round --- std::endl; // 玩家 1 先攻击 player1.attackTarget(player2); // 如果玩家 2 仍存活则玩家 2 反击 if (player2.isAlive()) { player2.attackTarget(player1); } // 每回合结束后输出双方状态 std::cout \nCurrent Status: std::endl; player1.showStatus(); player2.showStatus(); round; // 回合数加 1 } // 输出最终结果 std::cout \n Battle Result std::endl; if (player1.isAlive()) { std::cout player1.name wins! std::endl; } else if (player2.isAlive()) { std::cout player2.name wins! std::endl; } else { std::cout Both characters have fallen. std::endl; } return 0; // 程序正常结束 }这版代码比原本的固定伤害的版本更有趣因为每次运行的结果都可能不同。读者在运行程序时能够直观看到对象之间的交互并不是机械重复而是会受到随机事件影响同时Character仍然是类player1和player2仍然是对象每个对象有各自独立的生命值和攻击范围成员函数负责操作对象状态4. 代码讲解4.1.classclass Character { public: std::string name; // 角色名称 int health 100; // 角色生命值默认 100 int minAttack 10; // 最小攻击力 int maxAttack 20; // 最大攻击力 // 显示当前角色状态 void showStatus() const { std::cout name | Health: health | Attack Range: minAttack - maxAttack std::endl; } // 判断角色是否存活 bool isAlive() const { return health 0; } // 生成一次随机伤害并附带 miss / critical 判定 int generateDamage(bool isMiss, bool isCritical) const { // 先默认本次攻击不是 miss也不是暴击 isMiss false; isCritical false; // 生成 0~99 的随机数用于判定事件概率 int eventRoll rand() % 100; // 15% 概率 miss伤害直接为 0 if (eventRoll 15) { isMiss true; return 0; } // 在 [minAttack, maxAttack] 区间内生成普通伤害 int damage minAttack rand() % (maxAttack - minAttack 1); // 20% 概率暴击双倍伤害 if (eventRoll 15 eventRoll 35) { isCritical true; damage * 2; } return damage; }基本语法: 使用class关键字定义类型后跟类型名称和一对大括号最后需要加分号。类本质是用户自定义类型。命名规则:1类型名称采用首字母大写2类型名称应为名词表示某一类事物3命名不应与已有名称冲突类型定义:class 只是对数据结构和行为的描述本身不生成对象只有创建对象实例化时才分配内存并运行构 造函数。访问控制private类内部可见隐藏实现细节public对外提供接口。4.2.public作用: public确保外部能够访问类中的成员。访问范围:外部指的是在类外部的函数中。特点:所有真正的代码都在函数中类定义只是描述方式。4.3 成员变量定义在面向对象编程中成员变量是指定义在类或结构体中的变量也被称为字段field。它们是类或结构体的一部分用于存储对象的状态。成员变量可以是公共的、私有的或受保护的它们的访问权限取决于它们所在的类或结构体的访问修饰符。成员变量可以是任何数据类型包括基本数据类型、自定义数据类型和引用类型。定义方式从C11开始可以在声明时赋初值 可以使用‘’或大括号初始化大括号初始化会进行类型检查。内存特性:成员变量只是声明不分配实际内存只有在创建对象时才会分配内存成员变量决定了对象的内存结 构。内存分配: 对象的内存空间就是其成员变量所占空间的总和如果没有成员变量对象也会占用1字节空间。但是编译器会进行内存对齐优化实际空间可能比计算的要大不影响代码访问需要注意。访问方法有两种方法对象访问.运算符和指针访问-运算符。4.4.类与对象的内存管理在 C 中对象并不总是存储在同一位置。根据创建方式的不同对象可能位于栈区stack或堆区heap两者在生命周期、管理方式以及使用场景上存在显著差异。1栈管理栈区对象自动创建与销毁当对象在函数内部以普通变量的形式定义时该对象通常分配在栈区。栈区由编译器自动管理其生命周期与作用域一致对象在进入作用域时创建在离开作用域时自动销毁并调用析构函数如果存在。这种方式无需手动释放内存安全性高不容易发生内存泄漏因此在大多数情况下都是首选。栈对象适用于生命周期明确、作用域清晰的场景例如局部计算、临时数据或函数内部使用的对象。由于栈空间分配和释放效率高性能也通常优于堆分配。但栈空间大小有限不适合创建体积过大或生命周期需要跨越作用域的对象。#include iostream #include string class Character { public: std::string name; Character(const std::string n) : name(n) { std::cout name created. std::endl; } ~Character() { std::cout name destroyed. std::endl; } }; void testStack() { Character hero(StackHero); // 在栈上创建对象 std::cout Inside function. std::endl; } // 函数结束hero 自动销毁 int main() { testStack(); std::cout Back to main. std::endl; return 0; }运行结果会显示对象在函数结束时自动析构。2堆管理当使用new运算符创建对象时对象会被分配在堆区。堆区内存不受作用域限制对象会一直存在直到程序显式调用delete释放为止。这种方式适用于需要跨越函数作用域、动态创建数量不确定或生命周期较长的对象。然而堆对象需要程序员手动管理内存。如果忘记释放就会造成内存泄漏如果重复释放则可能导致程序崩溃。因此在现代 C 中通常推荐使用智能指针来管理堆对象而不是直接使用裸指针和new/delete。#include iostream #include string class Character { public: std::string name; Character(const std::string n) : name(n) { std::cout name created. std::endl; } ~Character() { std::cout name destroyed. std::endl; } }; void testHeap() { Character* hero new Character(HeapHero); // 在堆上创建对象 std::cout Inside function. std::endl; delete hero; // 手动释放内存 hero nullptr; // 防止悬空指针 } int main() { testHeap(); std::cout Back to main. std::endl; return 0; }总结栈对象强调自动管理与高效性堆对象强调灵活性与可控生命周期。