272 - First Class https://g.co/gemini/share/0f8339f51e2e
你好!我是你的 C++ 老师。很高兴能引导你进入 C++ 的世界。今天,我们将一起学习 C++ 中一个非常核心且强大的概念 —— 类 (Class)。别担心,我会一步一步地详细讲解,确保你能够轻松理解。
让我们开始吧!
1. 标题和摘要 标题: C++ 入门:创建你的第一个类 (Class) - 打造自定义数据类型
摘要: 本节课将向你介绍 C++ 中的类 (Class),它是一种允许我们创建自定义数据类型(就像 int
或 double
一样,但由我们自己设计)的机制。我们将通过创建一个表示“圆柱体”的类作为例子,学习如何定义类的属性(成员变量)和行为(成员函数),以及如何控制对这些成员的访问权限(public
与 private
)。最后,你将学会如何使用你创建的类来创建对象 (Object) 并在程序中使用它们。
2. 详细讲解 a. 为什么需要类 (Class)?
到目前为止,我们已经使用过 C++ 的一些内置(或称为基本)数据类型 (Type),比如 int
(整数), double
(双精度浮点数), bool
(布尔值) 等。我们可以这样使用它们:
C++
1 2 int age = 30; double price = 99.9;
这里,int
和 double
是类型,age
和 price
是变量名。
但是,如果我们想表示更复杂的事物呢?比如,想在程序里表示一个“人”,一个人可能有名字、年龄、地址等信息。或者像我们今天要做的,表示一个“圆柱体”,它有底面半径 (base radius) 和高 (height)。
C++ 的基本类型无法直接、完整地表示这些复杂概念。这时,我们就需要一种方法来创建我们自己的、能够封装(组合)多个数据和相关操作的类型。这就是 类 (Class) 发挥作用的地方!类是创建自定义类型的蓝图 (blueprint)。
b. 定义一个简单的类:以圆柱体为例
想象一个圆柱体。定义一个圆柱体需要哪些信息?通常是它的 底面半径 (base radius) 和 高 (height) 。有了这两个信息,我们就可以做很多事情,比如计算它的底面积(公式:π r²)或体积(公式:底面积 高)。
在 C++ 中,我们可以使用 class
关键字 (keyword) 来定义一个表示圆柱体的类。基本语法如下:
C++
1 2 3 4 // 定义类的语法 class ClassName { // 成员 (Members) }; // <-- 注意!这里必须有一个分号
让我们来定义 Cylinder
(圆柱体)类:
C++
1 2 3 4 5 6 // 引入常量 PI (圆周率) const double PI = 3.1415926535; // 实际项目中可能有更精确的方式定义 PI class Cylinder { // 类的内容会放在这里 }; // <-- 千万不要忘记这个分号!
c. 类成员:成员变量和成员函数
一个类通常包含两个主要部分:
成员变量 (Member Variables): 这些变量代表了类的属性或状态。对于 Cylinder
类,我们需要存储底面半径和高。我们可以选择 double
类型来存储它们,因为半径和高可能是小数。
C++
1 2 3 4 5 6 class Cylinder { public: // 先暂时设为 public,后面会解释 // 成员变量 (属性) double baseRadius {0.0}; // 使用花括号初始化为 0.0 double height {0.0}; // 使用花括号初始化为 0.0 };
我们在这里把 baseRadius
和 height
定义为 double
类型。
{0.0}
是一种初始化方式,确保创建圆柱体对象时,这些值有一个默认的初始状态。
成员函数 (Member Functions) / 方法 (Methods): 这些函数定义了类的行为或操作。它们通常会使用类的成员变量来完成某些任务。对于 Cylinder
类,我们可以定义一个函数来计算体积。
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 class Cylinder { public: // 稍后解释 public // 成员变量 double baseRadius {1.0}; // 给个默认值 double height {1.0}; // 给个默认值 // 成员函数 (行为/方法) double volume() { // 体积 = PI * 半径 * 半径 * 高 // 注意:这里可以直接访问同一个类中的成员变量 baseRadius 和 height return PI * baseRadius * baseRadius * height; } };
volume()
函数返回一个 double
类型的值(体积)。
它没有参数(括号是空的)。
函数体内,它直接使用了成员变量 baseRadius
和 height
来进行计算。这是关键点:成员函数可以自由访问同一个类的其他成员(变量或函数),无论它们是 public 还是 private。
d. 访问控制:public
(公有) 与 private
(私有)
你可能注意到上面代码中出现了 public:
这个词。这是一个 访问修饰符 (access specifier) 。它决定了类的哪些成员可以从类的 外部 (比如 main
函数)被访问。
public
(公有): 在 public:
后面声明的成员(变量或函数)可以被类的外部代码自由访问。
private
(私有): 在 private:
后面声明的成员只能被 类内部 的成员函数访问。它们对类的外部是隐藏的。
重要规则:默认情况下,类的所有成员都是 private
的!
如果我们不写 public:
,像这样:
C++
1 2 3 4 5 6 7 8 9 class Cylinder { // 没有写 public 或 private double baseRadius {1.0}; double height {1.0}; double volume() { return PI * baseRadius * baseRadius * height; } }; // 分号不能少
那么 baseRadius
, height
, 和 volume()
默认都是 private
的。这意味着,如果你在 main
函数中尝试访问它们,编译器会报错!
C++
1 2 3 4 5 6 7 8 int main() { Cylinder cylinder1; // 创建一个 Cylinder 对象 // 下面的代码会编译失败,因为 volume() 是 private 的 (默认) // std::cout << "Volume: " << cylinder1.volume() << std::endl; // 下面的代码也会编译失败,因为 baseRadius 和 height 是 private 的 // cylinder1.baseRadius = 10.0; return 0; }
良好的设计实践:
通常,我们会将 成员变量设为 private
,以保护类的内部状态不被外部随意修改,这叫做 数据封装 (Data Encapsulation) 。然后,提供 public
的成员函数 作为外部与类交互的接口 (interface)。
修改后的 Cylinder
类(更好的设计):
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 const double PI = 3.1415926535; class Cylinder { private: // 明确声明成员变量为私有 (虽然默认也是 private) double baseRadius {1.0}; double height {1.0}; public: // 公共接口 // 成员函数来计算体积 double volume() { // 仍然可以访问 private 成员,因为 volume() 是类的一部分 return PI * baseRadius * baseRadius * height; } // 我们可能需要提供 public 函数来安全地设置或获取私有成员的值 // (这些称为 Setters 和 Getters,我们稍后会学) // 例如,添加一个函数来设置半径和高 void setDimensions(double r, double h) { if (r > 0 && h > 0) { // 可以加入验证逻辑 baseRadius = r; height = h; } } // 添加一个函数来获取半径 (Getter) double getRadius() { return baseRadius; } // 添加一个函数来获取高度 (Getter) double getHeight() { return height; } };
现在 baseRadius
和 height
是 private
的,不能从 main
函数直接访问(如 cylinder1.baseRadius = 10;
会失败)。
volume()
, setDimensions()
, getRadius()
, getHeight()
是 public
的,可以从 main
函数调用。
setDimensions()
提供了一个受控的方式来修改内部的私有数据。
e. 使用类:创建对象 (Object)
类本身只是一个蓝图。要真正使用它,我们需要根据这个蓝图创建具体的实例,这些实例称为 对象 (Object) 。
创建对象就像声明一个普通变量一样,只是类型是我们自己定义的类名:
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <iostream> // 需要包含 iostream 来使用 std::cout int main() { // 创建一个 Cylinder 类的对象,名为 cylinder1 Cylinder cylinder1; // 这会使用默认的 baseRadius=1.0, height=1.0 // 调用 public 成员函数 volume() std::cout << "Cylinder 1 Volume (default): " << cylinder1.volume() << std::endl; // 输出默认体积 // 使用我们添加的 public setter 函数来修改尺寸 cylinder1.setDimensions(10.0, 3.0); // 设置半径为 10,高为 3 // 再次计算并打印体积 std::cout << "Cylinder 1 Volume (10, 3): " << cylinder1.volume() << std::endl; // 使用 getter 函数获取值 std::cout << "Cylinder 1 Radius: " << cylinder1.getRadius() << std::endl; std::cout << "Cylinder 1 Height: " << cylinder1.getHeight() << std::endl; // 尝试直接访问 private 成员(会失败) // cylinder1.baseRadius = 5.0; // 编译错误!baseRadius 是 private 的 // 可以创建多个对象 Cylinder cylinder2; cylinder2.setDimensions(2.0, 5.0); std::cout << "Cylinder 2 Volume (2, 5): " << cylinder2.volume() << std::endl; return 0; }
我们使用 Cylinder cylinder1;
创建了一个 Cylinder
对象。
我们使用 点运算符 (.
) 来访问对象的 public
成员(函数)。例如:cylinder1.volume()
,cylinder1.setDimensions(10.0, 3.0)
。
每个对象(cylinder1
, cylinder2
)都有自己的一套成员变量副本。修改 cylinder1
的半径和高不会影响 cylinder2
。
f. 回顾总结
类是创建自定义类型的蓝图。
类包含成员变量(属性)和成员函数(行为)。
public
成员可以从类外部访问,private
成员只能从类内部访问。
默认情况下,类成员是 private
的。
良好的实践是将数据成员设为 private
,并提供 public
函数接口。
使用类名可以创建对象(类的实例)。
使用点运算符 (.
) 访问对象的 public
成员。
3. 详细代码示例 下面是包含 Cylinder
类定义和 main
函数使用示例的完整代码:
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 #include <iostream> // 用于输入输出 (std::cout, std::endl) #include <cmath> // 在某些系统或标准下可能需要包含 cmath 来获取更精确的 PI 或数学函数 // 定义常量 PI const double PI = 3.141592653589793; // 定义 Cylinder 类 class Cylinder { private: // 私有成员:类的内部数据,对外部隐藏 double baseRadius; // 底面半径 double height; // 高 public: // 公有成员:类的外部接口 // 构造函数 (我们会在后续课程学习,这里先提供一个简单的默认设置) // 构造函数用于初始化对象,这里我们设置默认值 Cylinder() { baseRadius = 1.0; // 默认半径 height = 1.0; // 默认高度 std::cout << "Cylinder object created with default dimensions (r=1, h=1)" << std::endl; } // 带参数的构造函数 (允许在创建对象时指定尺寸) Cylinder(double r, double h) { std::cout << "Cylinder object created with dimensions (r=" << r << ", h=" << h << ")" << std::endl; // 使用 setDimensions 来进行可能的验证 setDimensions(r,h); } // 成员函数:计算体积 double volume() { // volume 函数可以访问 private 成员 baseRadius 和 height return PI * baseRadius * baseRadius * height; } // 成员函数:设置圆柱体的尺寸 (Setter 方法) void setDimensions(double r, double h) { // 可以添加检查,确保半径和高度是有效的(例如,大于0) if (r > 0 && h > 0) { baseRadius = r; height = h; std::cout << "Dimensions set to: radius = " << baseRadius << ", height = " << height << std::endl; } else { std::cout << "Error: Radius and height must be positive." << std::endl; // 可以选择保持原值或设置一个安全的默认值 if (baseRadius <= 0) baseRadius = 1.0; if (height <= 0) height = 1.0; } } // 成员函数:获取底面半径 (Getter 方法) double getRadius() { return baseRadius; } // 成员函数:获取高 (Getter 方法) double getHeight() { return height; } }; // 不要忘记类定义末尾的分号 // 主函数:程序入口 int main() { std::cout << "--- Creating cylinder1 (using default constructor) ---" << std::endl; Cylinder cylinder1; // 创建第一个 Cylinder 对象 (使用默认构造函数) std::cout << "Initial volume of cylinder1: " << cylinder1.volume() << std::endl; std::cout << "Initial radius of cylinder1: " << cylinder1.getRadius() << std::endl; std::cout << "Initial height of cylinder1: " << cylinder1.getHeight() << std::endl; std::cout << std::endl; // 打印空行 std::cout << "--- Modifying cylinder1 dimensions ---" << std::endl; cylinder1.setDimensions(10.0, 3.0); // 修改 cylinder1 的尺寸 std::cout << "Volume of cylinder1 after modification: " << cylinder1.volume() << std::endl; std::cout << std::endl; std::cout << "--- Creating cylinder2 (using parameterized constructor) ---" << std::endl; Cylinder cylinder2(2.0, 5.0); // 创建第二个 Cylinder 对象,并直接指定尺寸 std::cout << "Volume of cylinder2: " << cylinder2.volume() << std::endl; std::cout << "Radius of cylinder2: " << cylinder2.getRadius() << std::endl; std::cout << "Height of cylinder2: " << cylinder2.getHeight() << std::endl; std::cout << std::endl; std::cout << "--- Trying to set invalid dimensions for cylinder1 ---" << std::endl; cylinder1.setDimensions(-5.0, 10.0); // 尝试设置无效的半径 // 检查设置后的实际值(应该被setDimensions的逻辑处理了) std::cout << "Radius of cylinder1 after trying invalid set: " << cylinder1.getRadius() << std::endl; // 可能还是 10 或者被重置为 1 std::cout << "Height of cylinder1 after trying invalid set: " << cylinder1.getHeight() << std::endl; // 可能还是 3 或者被重置为 1 // 尝试直接访问 private 成员 (这会导致编译错误) // cylinder1.baseRadius = 20.0; // 取消注释这行会导致编译失败 // std::cout << cylinder1.height; // 取消注释这行也会导致编译失败 return 0; // 程序正常结束 }
4. Q&A 闪卡 (Flash Cards) 卡片 1
问题 (Q): 在 C++ 中,什么是类 (Class)?它有什么用途?
答案 (A): 类是用户定义的数据类型的蓝图或模板。它允许我们将数据(成员变量)和操作这些数据的函数(成员函数)捆绑在一起,用来创建我们自己的复杂数据类型,以模拟现实世界中的概念或实体。
卡片 2
问题 (Q): 什么是成员变量 (Member Variables) 和成员函数 (Member Functions)?
答案 (A): 成员变量是定义在类内部的变量,用于存储对象的状态或属性(例如圆柱体的半径和高)。成员函数是定义在类内部的函数,用于定义对象的行为或操作(例如计算圆柱体的体积)。
卡片 3
问题 (Q): public 和 private 访问修饰符有什么区别?类的成员默认是什么访问权限?
答案 (A): public 成员可以从类的外部(例如 main 函数或其他类)访问。private 成员只能从类的内部(即被同一个类的成员函数)访问。默认情况下,C++ 类的成员是 private 的。
卡片 4
问题 (Q): 如何根据一个类创建一个对象?如何访问对象的公有成员?
答案 (A): 创建对象就像声明一个变量:ClassName objectName;。访问对象的公有成员(变量或函数)使用点运算符 (.): objectName.publicMemberName。
5. 常见误解或错误
忘记类定义末尾的分号 (;
) : 这是初学者非常容易犯的错误,会导致奇怪的编译错误。
C++
1 2 3 class MyClass { // ... members ... } // <-- 错误!缺少分号
从类外部访问 private
成员 : 尝试在 main
函数或类的外部直接读写 private
成员变量会导致编译错误。必须通过 public
的成员函数(如 getters/setters)来间接访问。
C++
1 2 Cylinder c; // c.baseRadius = 10; // 错误!如果 baseRadius 是 private 的
混淆类 (Class) 和对象 (Object) : 类是蓝图,对象是根据蓝图创建出来的实体。你不能直接对类执行操作(比如计算“Cylinder类”的体积),而是要先创建类的对象,然后对该对象执行操作(计算 cylinder1
对象的体积)。
调用成员函数时忘记对象名和点运算符 : 成员函数必须通过某个对象来调用。
C++
1 2 // volume(); // 错误!必须指定是哪个对象的 volume() // cylinder1.volume(); // 正确
不必要地将成员变量设为 public
: 虽然这样做可以让外部直接访问,但破坏了封装性,使得类的内部状态容易被意外或恶意修改,通常是不良设计。应尽量保持数据成员 private
。
在成员函数内部访问成员变量时使用点运算符 : 在类的成员函数内部,可以直接使用成员变量的名字,不需要 this->
(虽然也可以用) 或者对象名。
C++
1 2 3 4 5 6 7 8 9 10 class Example { int value; public: void setValue(int v) { // value = v; // 正确且常用 // this->value = v; // 也正确,有时用于消除歧义 // Example.value = v; // 错误! // objectName.value = v; // 错误!(除非 objectName 是传进来的参数) } };
6. 编码练习 现在轮到你来实践了!下面是一个稍微不同的类 Box
(盒子),它有长 (length)、宽 (width) 和高 (height)。请你补全代码中的 // ???
部分,让程序能够正确编译和运行。
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <iostream> // 定义常量 (这里不需要 PI) class Box { private: // ??? 定义三个 double 类型的私有成员变量: length, width, height double length; double width; double height; public: // 构造函数,用于初始化盒子尺寸 Box(double l, double w, double h) { // ??? 在这里初始化成员变量 length, width, height // 可以直接赋值,或者调用一个设置函数(如果需要验证) length = l; width = w; height = h; std::cout << "Box created with dimensions: l=" << length << ", w=" << width << ", h=" << height << std::endl; } // ??? 定义一个名为 'volume' 的公有成员函数 // ??? 它应该返回盒子的体积 (长 * 宽 * 高) // ??? 返回类型应该是 double double volume(){ return length*width*height; } // ??? 定义一个名为 'printDimensions' 的公有成员函数 // ??? 它不返回任何值 (void) // ??? 它应该打印出盒子的长、宽、高 void printDimensions(){ std::cout << "Box dimensions: length = " << length << ", width = " << width << ", height = " << height << std::endl; } }; // <-- 不要忘记这个分号! int main() { // ??? 创建一个 Box 对象,名为 'myBox',长宽高分别为 10.0, 5.0, 2.0 Box myBox(10.0,5.0,2.0); // ??? 调用 myBox 的 printDimensions 函数来打印它的尺寸 myBox.printDimensions(); // ??? 调用 myBox 的 volume 函数,并将结果打印到控制台 // ??? 打印格式: "The volume of the box is: [体积]" std::cout << "The volume of the box is: " << myBox.volume() << std::endl; return 0; }
请仔细阅读注释,尝试填充 // ???
标记的代码。完成后,将你的代码发给我,我会帮你检查并给出评分和反馈!加油!