当前位置: 首页 > news >正文

游戏引擎学习第42天

仓库: https://gitee.com/mrxiao_com/2d_game

简介

目前我们正在研究的内容是如何构建一个基本的游戏引擎。我们将深入了解游戏开发的每一个环节,从最基础的技术实现到高级的游戏编程。

角色移动代码

我们主要讨论的是角色的移动代码。我一直希望能够使用一些基本的数学和游戏开发的基本矢量数学技术。然而,直到昨晚,我才为大家做了一个数学概述,介绍了在游戏开发中涉及的各种类型的数学。今晚,我们将进行第一次的实验,展示如何使用一些这些数学原理来实现角色的移动。通过使用两个二维矢量映射,我将展示它们是如何运作的,以及我们如何利用它们来实现角色在游戏中的移动。这将是我们工作的起点,帮助我们在游戏中获得一些良好的角色动作。

回顾

让我们从这里开始,看看这些东西。我决定继续使用我正在使用的平板电脑,即使我可能需要用painter来画一些图和线条,因为我觉得这可能会帮助我更好地理解和解释一些概念。虽然使用钢笔可能会给我带来一些困难,但我们要试一试,也许至少在这一集里,我可以适应它。
回忆一下,如果你记得的话,这是我们所停留的地方。我们做了很多工作,关于一些基础的东西。我们测试了滚动,允许自己创造了一个巨大的地图。所以,你知道,我们做了稀疏样式的地图存储,也做了许多其他的事情。但我们现在必须开始考虑如何让我们的角色进行移动。我们现在的角色移动代码是最糟糕的,是最基本的,最令人讨厌的移动代码。我甚至没有提到他还没有进行动画,因为这也是我们要解决的问题,但我说的只是比这更低层次的东西,即使只是让角色进行基本的移动。

当前角色移动代码的问题

你可能会注意到,角色的移动感觉并不好。一个主要问题是角色没有任何动量。现在,基本上所有的事情都发生在你按下按键的时候,他只是随着你按住键移动。一旦你松开了按键,他就停了。此外,还存在一些奇怪的现象。例如,如果我以某个速度向下移动,我的速度是固定的。但如果我同时向右和向下移动,我实际上会以两者速度的结合下移动。这种现象在对角线上显得尤为奇怪,因为角色移动的速度会加快。这也是一种不太令人满意的现象,因为如果我向上移动,角色在穿越一个虚拟门后加速,去到下一个图层。

这些现象最终导致了很多问题。角色非常容易被各种小障碍物卡住,很难穿过门。任何微小的偏差都会导致角色完全停止。这些问题使得玩家的移动感觉很糟糕,这就是我们希望改进的地方。
在这里插入图片描述

我们想要做的事情

所以除了这些问题,我们还希望做的就是让玩家的体验更有趣。当他们在游戏中移动角色时,这个动作应该更具吸引力,因为在这样的游戏中,玩家需要不断移动角色,躲避敌人等。他们的角色始终在屏幕上移动,从左到右,他们一直在进行这些操作。因为移动角色是他们游戏中的主要动作之一,所以我们希望提供更多的操作感和互动,让玩家觉得他们的角色更有趣、更充实。

什么使得游戏具有吸引力

所以,视觉和触觉反馈对于游戏而言是非常重要的,因为它们使游戏变得有趣。当玩家在游戏中移动时,他们需要能够感受到这种反馈,这会让他们觉得自己真正参与其中。视频游戏不同于传统的棋盘游戏,它通过音频视觉反馈来吸引玩家。这些反馈不仅仅是表面的,它们让玩家感受到动作的真实性和游戏的吸引力。一个好的游戏不仅仅是机制本身,还包括了让玩家有兴趣继续玩下去的那些细节。这些细节包括移动感、跳跃的感觉等,确保玩家在每个操作时都能体验到愉悦和成就感。我们希望在接下来的时间里,通过优化角色的移动方式来实现这种吸引力。

为什么需要向量数学

我们正在学习如何使用基本的矢量数学来改善角色的移动。通过这种方式,我们可以使角色在游戏中的运动更加流畅、更加符合玩家的需求,这样玩家在操作角色时能够感受到视觉和触觉上的满足感。这种方式的运动设计不仅能够让角色移动起来感觉很好,还能够激发玩家的兴趣,使他们更想操作这个角色。这就是我们目前所要达到的目标,即让角色的移动既有吸引力又令人满意,使玩家在每次操作时都感到愉悦。

应该开始修复的内容

我们要解决角色在移动时的动量问题,让他不会瞬间停下来,然后再开始,同时也解决他在对角线方向移动时速度加快的问题。通过学习基本的向量数学,我们可以轻松地修正这些问题。这将使角色在所有方向上移动更加一致,改善玩家的体验。让我们开始尝试解决这些问题吧。

向量数学简介

我们正在讨论矢量在游戏开发中的应用。向量不仅帮助我们处理诸如玩家的速度和方向等变量,还使得它们之间的关系变得更加紧密。例如,在处理角色的移动时,如果我们只看水平或垂直方向,它们就像独立的变量一样,但实际上它们是相互关联的。在实际操作中,角色的对角线移动涉及到两者的结合。这种耦合使得处理起来更直观、更高效,因为数学家早已为我们定义了所有可以对向量进行的基本操作。通过这些操作,我们可以更好地控制和优化游戏的体验。
在这里插入图片描述

草图

我们在讨论一个对象的运动时,特别是在游戏开发中,如何理解玩家在屏幕上的移动。我们使用勾股定理来解释这个问题。玩家在游戏中被视为在一个二维空间中移动,通过结合x和y轴的移动来创建一个对角线路径。这种对角线路径并不是玩家实际移动的两个单独的方向,而是这两个方向的综合表现。因此,尽管玩家在x和y轴上移动的距离分别为d和d,最终的路径长度却是勾股定理的平方根。

这解释了为什么玩家可能会看到比实际移动更长的路径,这被称为“幻影远距”。它并不是两个距离的简单相加,而是形成了一个更长的路径,这是因为它在两者之间形成了一个对角线。因此,尽管它们的移动速度可能是相同的,玩家看到的路径却是它们的勾股定理计算得出的更长的距离。

在游戏开发中,这一理解是重要的,因为它帮助我们优化运动轨迹,使得玩家感觉到的游戏体验更加真实。

在这里插入图片描述

移动标量问题

在对角线移动时,我们可以使用勾股定理(a² + b² = c²)来计算目标距离(d)。我们首先通过平方根将这个距离除以2来得到一个比例因子(v)。然后,基于该因子,我们可以在移动过程中将它们相乘来确定实际的移动量。这一过程允许我们计算在对角线方向上进行相等移动的需要的速度或距离。
在这里插入图片描述

v 2 + v 2 = d 2 d 2 = 2 v 2 v = d 2 2 v = d 2 2 v = d 2 2 v = 0.7071067811865475 ∗ d v^2 + v^2 = d^2 \\[1em] d^2 = 2v^2 \\[1em] v = \sqrt{\frac{d^2}{2}} \\[1em] v = d\frac{\sqrt{2}}{2} \\[1em] v = d\frac{\sqrt{2}}{2} \\[1em] v = 0.7071067811865475*d v2+v2=d2d2=2v2v=2d2 v=d22 v=d22 v=0.7071067811865475d

转向向量

我们讨论了矢量如何帮助我们简化问题。矢量实际上是多个信息片段的结合,允许我们以直观的方式操作这些片段,而无需关注低级数学细节。例如,当我们使用矢量时,不再需要思考逐步运动,而是可以直接从原点到目的地的直接移动。矢量概念在多维空间中同样适用,无论是二维、三维,甚至更多维度。它们帮助我们在更高层次上思考和处理问题,使得游戏开发中的各种计算变得更加高效和简洁。矢量不仅仅是简单的数学工具,它们在我们的工作中赋予了深刻的概念和语义。

转向向量(direction vector)是表示一个向量在几何空间中的方向的向量。它通常通过两个或多个点之间的差异来定义。例如,给定两个点 $A(x_1, y_1, z_1)$ 和 $B(x_2, y_2, z_2)$,其转向向量 $\vec{D}$ 可以通过以下公式计算:

D ⃗ = ( x 2 − x 1 , y 2 − y 1 , z 2 − z 1 ) \vec{D} = (x_2 - x_1, y_2 - y_1, z_2 - z_1) D =(x2x1,y2y1,z2z1)

这种转向向量的计算方式反映了从点 $A$ 到点 $B$ 的方向和大小。

在更广泛的数学和物理学中,转向向量可以应用于描述运动方向,速度矢量,力矢量等。例如,在物理学中,一个物体的运动速度可以用一个转向向量表示,这个向量指示物体的运动方向和速度的大小。

举例:

假设点 $A(2, 3, 4)$ 和点 $B(5, 7, 10)$,我们可以计算它们之间的转向向量:

D ⃗ = ( 5 − 2 , 7 − 3 , 10 − 4 ) = ( 3 , 4 , 6 ) \vec{D} = (5 - 2, 7 - 3, 10 - 4) = (3, 4, 6) D =(52,73,104)=(3,4,6)

这个向量 $(3, 4, 6)$ 表示从点 $A$ 到点 $B$ 的方向,以及从 $A$ 到 $B$ 的距离(大小)。

向量符号

我们在讨论矢量时,主要是在谈论如何组合多个信息片段。矢量可以通过竖直排列的方式来书写,这与传统的水平排列不同。这种书写方式反映了矢量在多维空间中的位置和方向。例如,一个向量可以被视为一个二维或三维的矩阵,其列或行代表特定方向上的量。而这种书写方式的选择影响了我们对矢量进行操作时的理解和处理方式。因此,决定如何书写这些对象非常重要,特别是在保持数学一致性的前提下。尽管代码编辑器不允许在中间插入垂直排列的矢量,但这种写法仍然是描述和理解矢量的直观方式。在代码中,我们通常会以这种方式来书写向量,尽管它可能在代码编辑器中有些不便。总体来说,理解和使用矢量的这种方式有助于我们在处理多维数据时更高效。

向量和矩阵之间有着紧密的关系,特别是在数学和计算机科学领域中。理解它们的关系可以帮助我们更好地理解和操作多维数据。

向量

一个向量是由一个或多个数值组成的对象,它在数学中被用来表示方向和大小。例如,一个二维向量可以表示一个点在平面上的位置,而一个三维向量可以表示一个点在三维空间中的坐标。

  • 二维向量: $\vec{v} = \begin{pmatrix} x \ y \end{pmatrix}$
  • 三维向量: $\vec{v} = \begin{pmatrix} x \ y \ z \end{pmatrix}$

矩阵

矩阵是一组由数字(元素)组成的表格,可以用来表示多个向量的线性组合。矩阵的元素通常用来描述系统的线性关系。例如,一个二维矩阵可以表示一个线性变换,如旋转或缩放。

  • 二维矩阵:
    A = ( a 11 a 12 a 21 a 22 ) A = \begin{pmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{pmatrix} A=(a11a21a12a22)
  • 三维矩阵:
    B = ( b 11 b 12 b 13 b 21 b 22 b 23 b 31 b 32 b 33 ) B = \begin{pmatrix} b_{11} & b_{12} & b_{13} \\ b_{21} & b_{22} & b_{23} \\ b_{31} & b_{32} & b_{33} \end{pmatrix} B= b11b21b31b12b22b32b13b23b33

行列关系

  1. 向量可以看作是矩阵的“列”:

    • 一个向量 $\vec{v}$ 可以被看作是一个 $n \times 1$ 矩阵。
    • 例如,二维向量 $\vec{v} = \begin{pmatrix} x \ y \end{pmatrix}$ 是一个 $2 \times 1$ 矩阵。
  2. 矩阵的乘法:

    • 当一个向量乘以矩阵时,结果是另一个向量。
    • 如果矩阵 $A$ 的列数等于向量的大小,矩阵乘法的结果将是一个新的向量。
  3. 矩阵的行和列:

    • 矩阵的每一行可以看作是一个向量。
    • 矩阵的每一列也可以视为一个向量。
    • 例如,对于一个 $2 \times 3$ 矩阵:
      A = ( 1 2 3 4 5 6 ) A = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{pmatrix} A=(142536)
      每一列 $\begin{pmatrix} 1 \ 4 \end{pmatrix}$ 和 $\begin{pmatrix} 2 \ 5 \end{pmatrix}$ 都是向量。
  4. 转置矩阵:

    • 矩阵的转置操作(将矩阵的行转变为列)常用于向量和矩阵之间的操作。例如,矩阵的转置 $A^T$ 将 $A$ 中的每个列变成行。

行列操作示例

  • 向量和矩阵的加法:

    • 设 $\vec{v} = \begin{pmatrix} x \ y \end{pmatrix}$ 和 $\vec{w} = \begin{pmatrix} u \ v \end{pmatrix}$ 是两个向量。
    • $\vec{v} + \vec{w} = \begin{pmatrix} x + u \ y + v \end{pmatrix}$
  • 向量和矩阵的乘法:

    • 设矩阵 $A$ 是一个 $2 \times 3$ 矩阵,$\vec{v} = \begin{pmatrix} x \ y \end{pmatrix}$ 是一个 $2 \times 1$ 向量。
    • 矩阵乘法得到的向量为:
      A v ⃗ = ( 1 2 4 5 ) ( x y ) = ( 1 x + 2 y 4 x + 5 y ) A\vec{v} = \begin{pmatrix} 1 & 2 \\ 4 & 5 \end{pmatrix} \begin{pmatrix} x \\ y \end{pmatrix} = \begin{pmatrix} 1x + 2y \\ 4x + 5y \end{pmatrix} Av =(1425)(xy)=(1x+2y4x+5y)

这种行列关系使得向量和矩阵的结合成为处理多维数据和进行各种线性变换的基础工具。

将向量例程添加到代码中

这是一个非常详细的讲解,说明了如何在编程和数学中讨论向量乘法、运算符重载,以及代码实现时的实用性。以下是这段话的重点总结和主要思想:


1. 关于向量乘法

  • 点积和叉积: 向量乘法有多种形式,点积和叉积是其中最常用的两种。它们有具体的几何和实际意义,比如用于计算投影或方向。
  • 逐分量乘法: 这是简单的将对应分量相乘,这种操作在某些领域有用,例如处理颜色或掩码,但对表示空间关系的操作来说意义不大。

2. 运算符重载的实用性

  • 为什么在向量中有用: 在实现向量时,运算符重载(如 +-)可以让代码看起来更像数学表达式。
  • 局限性: 尽管在向量中方便,但在其他场景中,运算符重载未必同样有效。

3. 实现向量操作

作者的目标是:

  1. 创建一个简单的向量结构,包含 xy 分量。
  2. 提供既能按名称访问(如 .x)又能按索引访问(如 [0][1])的能力。
  3. 为常见操作定义运算符重载,如加法、减法和取反。

4. 示例实现代码

以下是这段思想的可能代码实现示例(假设用 C++ 实现):

#include <iostream>struct Vector2 {float x, y;// 通过索引访问float& operator[](int index) {return (index == 0) ? x : y;}// 运算符重载:加法Vector2 operator+(const Vector2& other) const {return {x + other.x, y + other.y};}// 运算符重载:减法Vector2 operator-(const Vector2& other) const {return {x - other.x, y - other.y};}// 运算符重载:取反Vector2 operator-() const {return {-x, -y};}
};int main() {Vector2 v1 = {1.0f, 2.0f};Vector2 v2 = {3.0f, 4.0f};Vector2 sum = v1 + v2;Vector2 diff = v1 - v2;Vector2 neg = -v1;std::cout << "Sum: (" << sum.x << ", " << sum.y << ")\n";std::cout << "Difference: (" << diff.x << ", " << diff.y << ")\n";std::cout << "Negation: (" << neg.x << ", " << neg.y << ")\n";return 0;
}

5. 学习的关键点

  • 理解向量操作的几何意义: 比如点积计算投影长度,叉积计算面积等。
  • 运算符重载的设计: 保持代码的可读性,同时兼顾功能性。
  • 逐步实现复杂功能: 从基本操作(加、减、取反)开始,再扩展到更高级的矩阵运算和向量操作。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

联合类型在C++中的工作原理及其缺点?

C++ 中的 Union(联合体)

Union 的工作原理:
在 C++ 中,union 是一种特殊的数据类型,它允许多个数据成员共享同一块内存。Union 的特性是同一时间只能有一个成员存储有效的数据,因为所有成员共用同一片内存空间。这种机制常用于内存优化或处理底层数据操作。

例如,一个包含 intfloat 成员的联合体,写入一个成员会覆盖另一个成员的值,因为它们的内存地址相同。

示例:

union Data {int intValue;float floatValue;
};

访问和修改联合体的成员:

Data data;
data.intValue = 5; // 赋值给 intValue
std::cout << data.floatValue; // 读取 floatValue(以 float 格式解释同一内存)

使用 Union 的缺点:

  1. 类型安全性低: 如果访问的是最近没有被赋值的成员,可能会导致未定义行为。这使得联合体的使用需要非常小心。

  2. 初始化复杂: C++ 中的联合体不支持同时对多个成员进行直接初始化。如果需要更复杂的初始化,必须定义构造函数或额外的方法,这会让语法变得不一致。

  3. 兼容性问题: 现代 C++ 的某些特性(如使用大括号的初始化方法或某些运算符重载)在联合体中可能无法直接使用。

  4. 限制现代功能: 联合体不能与某些现代 C++ 特性(如构造函数、析构函数和虚函数)完全兼容,这限制了它在复杂类型中的使用。

  5. 容易出现错误: 联合体中的数据解释依赖于开发者的正确访问,如果预期的数据类型和实际存储的数据类型不匹配,就可能引发难以定位的错误。

实际问题示例:
假设想设计一个联合体,既能存储一对 float 值(如 xy),又能通过数组的方式访问这些值。在 C++ 中,由于联合体初始化和语法限制,无法直接实现这样的灵活访问方式。

一种解决方法是定义自定义运算符来访问联合体成员,但这会让代码看起来与普通的结构体语法不一致,降低可读性和直观性。

总结:
虽然 Union 提供了内存效率高、灵活性强的特性,但其缺点包括类型安全性低、对现代 C++ 特性的支持不足以及复杂的语法要求。这使得 Union 在实际应用中往往显得笨拙。只有在对其行为有深刻理解的前提下,才能合理避免相关陷阱并有效利用其特性。

关于向量的约定

矢量和矩阵约定的标准化

关于矢量和矩阵约定的标准化问题,有人提到,某种约定大约在 1993 年得到了统一,尤其是涉及矢量和列向量在空间点表示中的使用。然而,这一时间点是否准确存在疑问,因为数学早在这之前已经在大量应用中使用列向量来表示空间中的点。

矢量和矩阵在数学中的传统使用
  • 矢量和列向量:在数学和几何中,矢量的使用可以追溯到更早的时期。尤其是在描述点的位置或操作变换时,列向量作为一种标准方式被广泛接受和应用。
  • 矩阵的使用:矩阵在数学中的历史悠久,并在 19 世纪就已经被用于线性变换的表示。列向量的使用通常与这种矩阵操作密切相关。
关于标准化时间点的质疑

1993 年被认为是矢量和矩阵约定标准化的一个时间点,但这种说法可能存在问题:

  1. 数学的传统:矢量的列向量表示形式和其在空间中的几何意义早已被数学家所熟知并广泛采用。线性代数等领域中的列向量概念,显然在1993年之前就已经是数学和物理领域的常识。
  2. 实际应用:计算机图形学、工程力学以及其他科学领域在 20 世纪中叶甚至更早就开始广泛使用矩阵和矢量操作,这意味着标准化的实践在很久以前已经存在。
可能的解释
  • 行业或技术标准化:1993 年的标准化可能指的是某个行业或某种技术的具体约定,而非数学本身。例如,计算机图形学领域在这一时期可能对矩阵和矢量的约定进行了进一步统一,以便跨平台或跨工具的协作。
  • 误解来源:将一个具体领域的标准化时间误认为是整个数学领域的标准化可能导致了这一时间点的混淆。
结论

矢量和矩阵的列向量约定作为数学的一部分,其历史可以追溯得更早。1993 年可能仅仅是某一行业或特定技术中实现了标准化,但这一时间点无法否认数学界早期的广泛使用和接受。

在第一个向量版本中,是否只缺少了联合的匿名结构?

联合类型的使用和挑战

在初期的实现中,联合类型代码存在一个问题:缺少匿名结构将字段封装起来,导致字段在内存中重叠的问题。这种实现方式可能并未按照预期工作,因为数据的布局方式并未明确定义。

关于匿名结构的使用
  • 问题发现:在代码中添加匿名结构后,字段的重叠问题得以解决。这是一个好的解决方案,可以正确实现对联合类型的封装。
  • 测试效果:在封装匿名结构后,代码的功能按预期运行,问题似乎得到了修复。这表明匿名结构在这种情况下是一个必要的调整。
匿名结构的兼容性问题
  • 编译器行为差异:匿名结构在不同编译器中的表现可能存在差异。在 Visual Studio 中,这种实现没有问题,但可能在某些其他编译器(如 LLVM 或其他工具链)中引发警告或错误。
  • 具体问题:之前的尝试表明,匿名结构在某些环境中会导致编译器投诉。因此,这种解决方案可能并不具有普遍的适用性。
联合类型的局限性
  • 构造函数问题:联合类型在 C++ 中本身存在诸多局限,尤其是无法支持非平凡类型的构造函数和析构函数。如果试图在联合中使用复杂类型,可能会引发意料之外的问题。
  • 复杂性管理:为了在不同编译器之间保持一致性,需要在实现时尽量避免使用复杂的特性,选择简单、清晰的方式来规避潜在问题。
实践中的权衡

在这种有限的场景中,如果代码在两个主流编译器上都能正确编译且表现一致,则可以考虑接受这种实现。考虑到使用的联合类型不会涉及复杂的操作(如构造函数或析构函数),该实现可能足够安全。

后续建议
  • 测试环境多样性:确保在多种编译器(如 GCC、LLVM)环境下进行充分测试,避免仅依赖 Visual Studio 的表现。
  • 关注维护性:尽量使用明确、清晰的结构组织代码,减少潜在的兼容性问题。
  • 避免复杂操作:在联合类型中,不应引入复杂类型或需要特别管理生命周期的对象,以确保代码的稳定性和跨平台可移植性。

通过匿名结构的调整,可以在一定程度上优化联合类型的使用,但其兼容性和局限性仍需小心应对。

在这里插入图片描述

在v2上不使用构造函数是否有性能原因?

关于构造函数的使用与否
  1. 选择不使用构造函数的原因
    • 简化代码:避免引入构造函数可以减少额外的代码编写和维护成本。
    • 语法灵活性:在没有构造函数的情况下,可以完全省略初始化代码,而使用默认的变量声明方式。
    • 避免复杂性:一旦引入构造函数,可能需要编写多个版本,例如默认构造函数、带参数的构造函数等。
  2. 性能影响
    • 构造函数的使用通常不会对性能有显著影响,因为编译器通常能够优化这部分代码。
    • 选择不使用构造函数更多是基于代码风格和偏好,而不是性能考量。
  3. 替代方案
    • 通过直接声明或零初始化,可以实现与构造函数类似的功能,但代码更加简洁。
    • 例如,将变量初始化为零或未初始化的状态,利用编译器的默认行为满足需求。

对象成员的私有或公共性何时会发挥作用?

关于对象成员的公开和私有访问权限

成员访问权限的作用
  1. 公开和私有的区分
    • 公共成员可以在类的外部访问和修改。
    • 私有成员只能由类的内部方法或友元函数访问。
  2. 私有访问的目标
    • 限制代码中可以修改类成员的范围。
    • 确保只有特定部分的代码可以访问某些敏感数据或逻辑。
    • 通过缩小访问范围来减少潜在的错误。
私有成员的使用场景
  1. 预防错误
    • 私有成员可以防止其他程序员或自身在不该修改某些部分时意外更改。
    • 限制外部代码对类内部实现的直接操作。
  2. 代码健壮性
    • 将某些实现细节隐藏在类内部,使代码对用户更加友好且易于维护。
    • 避免意外的状态改变,从而减少调试的复杂性。
对使用私有的不同看法
  1. 不使用私有的观点
    • 一些开发者认为私有成员限制了灵活性,使得外部代码操作类变得更困难。
    • 如果对代码结构有足够的控制,并对潜在错误风险有较强的信心,可能不会优先考虑使用私有。
    • 假如代码从未出现任何错误,则不使用私有访问控制也不会造成问题。
  2. 支持使用私有的观点
    • 其他开发者认为私有可以作为一种安全机制,有效减少意外修改和潜在问题。
    • 在协作开发中,私有访问权限可以为团队成员提供明确的操作边界。
使用私有的取舍
  1. 权衡点
    • 私有成员可以提供额外的保护,但同时也可能增加类的复杂性。
    • 在某些场景下,过度使用私有可能导致需要更多辅助代码(如友元函数或公共接口)来完成简单任务。
  2. 个人选择
    • 是否使用私有访问权限通常取决于开发者的风格和项目需求。
    • 对于一些项目,开放的成员访问可能更实用;而对于另一些注重稳定性和代码安全的项目,私有可能更加合适。
总结
  • 公共和私有成员的区分是为了平衡灵活性和安全性。
  • 私有访问权限主要用于减少潜在的错误风险,通过限制访问范围来增强代码的健壮性。
  • 是否使用私有成员是开发者的选择,根据项目需求和开发风格灵活决定。
  • 对于需要频繁操作类成员的场景,可能更倾向于开放访问权限;而在保护关键数据时,私有权限更具优势。

应该能够在对象外部进行复合赋值。

关于复合赋值操作和成员定义的调整

实现复合赋值的方式
  1. 复合赋值的支持

    • 目标是在对象外部实现复合赋值运算。
    • 通过将相关操作定义为外部函数,可以实现这一目标。
    • 将第一个操作数作为引用传递(reference member)是关键。
  2. 操作位置的调整

    • 可以将复合赋值逻辑定义在外部,而无需嵌入类内部。
    • 这种方式减少了类的复杂性,同时保留了功能的灵活性。
调整过程中的探索
  1. 代码调整

    • 初步尝试是在类内部进行处理,但后续发现可以移至外部。
    • 通过测试和验证确认外部实现是可行的,并进行了必要的修改。
  2. 处理细节的回忆

    • 尽管熟悉基本概念,但在具体实现过程中可能会遗忘某些符号或语法。
    • 例如,与模板相关的语法,在没有频繁使用的情况下可能需要额外时间回忆。
关于模板和符号
  1. 符号和模板的学习曲线

    • 处理模板和符号时,回忆具体语法可能是一个挑战。
    • 即使曾经熟练使用模板,长时间未使用可能导致需要额外时间重新熟悉。
  2. 具体问题的处理

    • 针对每个符号或操作进行测试和调整,例如如何正确处理某些算术操作。
    • 逐步调试并修正错误,确保输出结果与预期一致。
复合赋值示例的确认
  1. 操作逻辑验证

    • 通过实际编写和测试,确认了复合赋值操作逻辑的正确性。
    • 结果显示可以成功在外部实现复合赋值,并返回正确的结果。
  2. 调整和优化

    • 删除了初始测试中定义在类内部的冗余部分。
    • 最终优化了代码结构,将相关逻辑调整为外部实现。
总结
  • 复合赋值操作可以通过外部函数实现,简化了类的定义。
  • 确保第一个操作数作为引用传递是实现这一功能的关键。
  • 长期未使用某些符号或语法可能导致一定的回忆困难,但通过测试和调整可以快速解决问题。
  • 通过这种方式,可以提高代码的可读性和灵活性,同时保持功能的完整性。

在这里插入图片描述

如果使用竖线符号表示向量,如何不记事行列式?

在使用向量符号时,在矩阵中使用竖条来表示行列式是一个常见的做法。在我的线性代数中,通常使用方括号来表示矩阵和向量,而竖条则用于表示决定。对于一个矩阵或向量,我会用竖条来表示其行列式。

例如,若 A 是一个矩阵,则其行列式可以表示为 |A|。竖条内的表达式代表着矩阵 A 的行列式值,这种写法非常典型且直观。

这个符号 | 用于表示行列式不仅仅是为了便于书写和阅读,也是因为它们传达了数学运算中的一种明确性。例如,行列式的计算常用于线性变换和矩阵理论的其他方面。

总的来说,行列式在数学运算中并不常被频繁写入,因为它们主要在需要进行矩阵运算时才被考虑。因此,在写作和阅读过程中保持符号的简洁和直观是重要的。对于向量,我更倾向于使用直线表示,这种符号化方式最简洁,直观且不容易引起歧义。

你不能把整个向量结构作为联合吗?

在讨论中,人们提到将整个功能放在union中(即联合体)作为一个整体,而不是使用struct。这种方法的可行性引发了质疑,因为传统上,union并不常用于操作符重载或集成的操作。

在这个情况下,答案被认为是肯定的——即可以在union上实现这些功能,而不会出现问题。尽管从未尝试过这样的操作符重载或功能集成,这种方法看起来似乎是可行的。如果问到是否可以将操作符重载应用于union,回答可能是“我从来没有尝试过,但它应该工作”。

因此,在这种讨论中,union被视为一种类似于指导的工具,可以用来组织和管理操作。在这种情况下,union的结构和功能能够显现出其灵活性,并且在某些情况下,如操作符重载,甚至可能会产生意想不到的效果。整体来看,这种方法会一直被用到有问题为止,直到它不能再适用为止。

在这里插入图片描述

有没有向量数学书推荐?

在这里插入图片描述

“…/book/线性代数(第5版) (Gilbert Strang (吉尔伯特·斯特朗)).pdf”

如何在函数参数中添加 const 引用以避免按值复制?

在C++中,使用常量引用作为函数参数以避免按值复制并不会总是提高性能。尽管有时它可以帮助优化,因为编译器可能会在常量引用上应用一些优化策略,但这种情况并不总是显而易见。通常,成本优化对编译器来说并不是一个经常使用的优化策略,因为它依赖于特定的编译器实现和特定的代码路径。

在实际编程过程中,使用常量引用主要是为了减少内存复制操作,从而避免临时变量的创建和销毁,这对于性能影响不大的情况下是有好处的。然而,在大多数情况,尤其是当涉及到指针和复杂的对象时,直接使用值传递可能更合适,编译器能够根据情况优化代码,而不需要额外的编译时成本。

因此,对于普通的编程任务来说,成本优化可能并不总是值得关注的,因为其帮助有限且依赖于具体情况。在性能关键的代码部分中,确实有可能需要考虑常量引用,以避免不必要的内存拷贝和复制操作。

相关文章:

游戏引擎学习第42天

仓库: https://gitee.com/mrxiao_com/2d_game 简介 目前我们正在研究的内容是如何构建一个基本的游戏引擎。我们将深入了解游戏开发的每一个环节&#xff0c;从最基础的技术实现到高级的游戏编程。 角色移动代码 我们主要讨论的是角色的移动代码。我一直希望能够使用一些基…...

elasticsearch设置密码访问

1 用户认证介绍 默认ES是没有设置用户认证访问的&#xff0c;所以每次访问时&#xff0c;直接调相关API就能查询和写入数据。现在做一个认证&#xff0c;只有通过认证的用户才能访问和操作ES。 2 开启加密设置 1.生成证书文件 /usr/share/elasticsearch/bin/elasticsearch-…...

阿里云-通义灵码:测试与实例展示

目录 一.引子 二.例子 三.优点 四.其他优点 五.总结 一.引子 在软件开发的广袤天地中&#xff0c;阿里云通义灵码宛如一座蕴藏无尽智慧的宝库&#xff0c;等待着开发者们去深入挖掘和探索。当我们跨越了入门的门槛&#xff0c;真正开始使用通义灵码进行代码生成和开发工作…...

开发者指南--RecyclerView显示数据列表和网格

一、RecyclerView的优势 RecyclerView 的最大优势在于&#xff0c;它对大型列表来说非常高效&#xff1a; 默认情况下&#xff0c;RecyclerView 仅会处理或绘制当前显示在屏幕上的项。例如&#xff0c;如果您的列表包含一千个元素&#xff0c;但只有 10 个元素可见&#xff0…...

Ajax--实现检测用户名是否存在功能

目录 &#xff08;一&#xff09;什么是Ajax &#xff08;二&#xff09;同步交互与异步交互 &#xff08;三&#xff09;AJAX常见应用情景 &#xff08;四&#xff09;AJAX的优缺点 &#xff08;五&#xff09;使用jQuery实现AJAX 1.使用JQuery中的ajax方法实现步骤&#xf…...

操作系统(5)进程

一、定义与特点 定义&#xff1a;进程是计算机中的程序关于某数据集合上的一次运行活动&#xff0c;是系统进行资源分配和调度的基本单位&#xff0c;是操作系统结构的基础。 特点&#xff1a; 动态性&#xff1a;进程是动态创建的&#xff0c;有它自身的生命周期&#xff0c;…...

力扣9. 回文数

给你一个整数 x &#xff0c;如果 x 是一个回文整数&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 回文数 是指正序&#xff08;从左向右&#xff09;和倒序&#xff08;从右向左&#xff09;读都是一样的整数。 例如&#xff0c;121 是回文&#xff0c;而…...

1_linux系统网络性能如何优化——几种开源网络协议栈比较

之前合集《计算机网络从入门到放弃》第一阶段算是已经完成了。都是理论&#xff0c;没有实操&#xff0c;让“程序猿”很难受&#xff0c;操作性不如 Modbus发送的报文何时等到应答和 tcp通信测试报告单1——connect和send。开始是想看linux内核网络协议栈的源码&#xff0c;然…...

C#—BitArray点阵列

C#—BitArray点阵列 在 C# 中&#xff0c;BitArray 类用来管理一个紧凑型的位值数组&#xff0c;数组中的值均为布尔类型&#xff0c;其中 true&#xff08;1&#xff09;表示此位为开启&#xff0c;false&#xff08;0&#xff09;表示此位为关闭。 当需要存储位&#xff08…...

特工找密码(蓝桥杯)

本来这题想用枚举暴力解的&#xff0c;但是运行总是超时&#xff0c;数值范围太大了~&#xff0c;所以该题不能用枚举进行暴力。 转换成二进制&#xff0c;我们判断一下其规律 注意&#xff1a;按位与是都为1时其值才为1&#xff0c;所以当x和y按位与的结果为2时&#xff0c;其…...

微信小程序--创建一个日历组件

微信小程序–创建一个日历组件 可以创建一个日历组件&#xff0c;来展示当前月份的日期&#xff0c;并支持切换月份的功能。 一、目录结构 /pages/calendarcalendar.wxmlcalendar.scsscalendar.jscalendar.json二、calendar.wxml <view class"calendar"><…...

A6919 基于java+SSM+mysql的区域物流管理系统设计与实现

的区域物流管理系统的设计与实现 1.摘要2.开发目的和意义3.系统功能设计4.系统界面截图5.源码获取 1.摘要 摘 要 随着当前我国市场经济和计算机互联网技术迅速发展&#xff0c;各行各业的销售和管理都在逐步转向着第三方物流服务&#xff0c;包括中通快递&#xff0c;申通&…...

Python大数据可视化:基于python的电影天堂数据可视化_django+hive

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 电影数据 看板展示 我的信息 摘要 电影天堂数据可视化是…...

美畅物联丨JS播放器录像功能:从技术到应用的全面解析

畅联云平台的JS播放器是一款功能十分强大的视频汇聚平台播放工具&#xff0c;它已经具备众多实用功能&#xff0c;像实时播放、历史录像回放、云台控制、倍速播放、录像记录、音频播放、画面放大、全屏展示、截图捕捉等等。这些功能构建起了一个高效、灵活且用户友好的播放环境…...

前端国际化实战:从需求到落地的完整实践

"我们要开拓东南亚市场了&#xff01;"产品经理小王兴奋地告诉我这个消息。作为技术负责人,我立刻意识到这意味着我们需要对整个系统进行国际化改造。说实话,虽然之前也做过一些多语言的项目,但面对一个正在运行的大型系统,国际化改造的挑战还是不小。 回想起上周的…...

MySQL 内置函数

字符串函数 concat(str1, str2, ...) 描述: 这个函数用于连接两个或多个字符串&#xff0c;返回一个新字符串。语法: concat(str1, str2, ...)注意点: 如果任意一个参数是null&#xff0c;则结果为null。可以连接任意数量的字符串。示例: select concat(first name: , first_…...

【Spring】日志类Logger的使用

在Spring框架中&#xff0c;日志记录是一个重要的组成部分&#xff0c;通常使用不同的日志框架来处理应用程序的日志。Spring 本身并直接提供一个名为Logger 的类&#xff0c;而是通过抽象的日志 API 让开发者能够选择和使用不同的日志实现&#xff08;如 Log4j、Logback、SLF4…...

动态高优先权优先进程调度

一、实验目的 目的&#xff1a;了解并掌握动态高优先权优先调度算法的理论&#xff0c;掌握动态优先权的设置方式。 任务&#xff1a;模拟实现动态高优先权优先的调度&#xff08;若数值越大优先权越高&#xff0c;每运行一个时间单位优先权-n&#xff0c;若数值越小优先权越高…...

【Linux SH脚本】LinuxCheck 应急检查信息脚本

LinuxCheck 1.下载地址 【Linux SH脚本】LinuxCheck 应急检查信息脚本 2.简介 LinuxCheck 是一个开源的自动化检查脚本&#xff0c;旨在快速检测 Linux 系统的安全配置和潜在问题。它支持多种发行版&#xff0c;能够扫描并生成详细的报告&#xff0c;涵盖用户管理、权限配置…...

Vue - route路由(router-link、useRoute、useRouter)

为了避免反复在 app.vue 中去修改引入的路径&#xff0c;当用了新的页面&#xff0c;想切换回老页面的时候&#xff0c;都需要去手动改变路径&#xff0c;那么有没有一种可能&#xff0c;可以在一个地方&#xff0c;把这些组件配置好&#xff0c;然后通过不同的路径&#xff0c…...

【HarmonyOS】鸿蒙应用实现手机摇一摇功能

【HarmonyOS】鸿蒙应用实现手机摇一摇功能 一、前言 手机摇一摇功能&#xff0c;是通过获取手机设备&#xff0c;加速度传感器接口&#xff0c;获取其中的数值&#xff0c;进行逻辑判断实现的功能。 在鸿蒙中手机设备传感器ohos.sensor (传感器)的系统API监听有以下&#xf…...

渗透测试工具 -- SQLmap安装教程及使用

随着网络安全问题日益严峻&#xff0c;渗透测试成为了保护信息安全的重要手段。而在渗透测试的众多工具中&#xff0c;SQLmap凭借其强大的自动化SQL注入检测和利用能力&#xff0c;成为了网络安全专家必备的利器。那么&#xff0c;你知道如何高效地使用SQLmap进行漏洞扫描吗&am…...

vue 前端使用fetch实现下载文件跨域

首先配置vite.config.js export default defineConfig({plugins: [vue(),],resolve: {alias: {: /src, // 根据你的项目结构进行设置},},server: {proxy: {/image-proxy: {target: https://你得代理服务器,changeOrigin: true,rewrite: path > path.replace(/^/image-proxy…...

AI与大数据的深度结合:驱动决策的革命性力量

引言&#xff1a;数字时代的决策挑战 在这个信息爆炸的数字时代&#xff0c;数据早已渗透到我们生活的方方面面。全球每天产生的数据量呈指数级增长&#xff0c;无论是用户的消费行为、设备的运行状态&#xff0c;还是社会热点的实时动态&#xff0c;这些信息的规模和复杂性前所…...

搭建C#开发环境

本文记录C#开发环境的搭建过程。 一、Windows系统 二、Ubuntu 运行以下命令安装.NET SDK&#xff0c; sudo add-apt-repository ppa:dotnet/backports sudo apt-get install -y dotnet-sdk-9.0网络资料 Install .NET on Windowshttps://learn.microsoft.com/en-us/dotnet/co…...

Gitlab分支合并及在本地解决冲突

文章目录 问题及解决参考 问题及解决 Gitlab分支合并时碰到了合并冲突的问题&#xff0c;进行了本地解决冲突的操作&#xff0c;并成功进行了合并。 在服务器端的冲突解决比较简单&#xff0c;在此不赘述&#xff0c;这里主要记录下在本地解决冲突的操作。 Gitlab冲突的根本…...

解决 “TypeError: ‘tuple‘ object cannot be interpreted as an integer“ 错误提示

错误背景 这个错误通常出现在期望一个整数时&#xff0c;却传入了一个元组&#xff08;tuple&#xff09;。Python 无法将元组解释为整数&#xff0c;因此会抛出 TypeError。 错误示例 python 复制代码 for i in (1, 2, 3): print(range(i)) 运行时会抛出如下错误&#xff1a;…...

OSPF协议

OSPF介绍 OSPF&#xff08;Open Shortest Path First&#xff0c;开放最短路径优先&#xff09;是一种用于互联网协议网络的链路状态路由协议。它属于内部网关协议&#xff08;IGP&#xff09;&#xff0c;主要用于单一自治系统&#xff08;AS&#xff09;内部的路由选择。在A…...

前端编辑器JSON HTML等,vue2-ace-editor,vue3-ace-editor

与框架无关 vue2-ace-editor有问题&#xff0c;ace拿不到&#xff08;brace&#xff09; 一些组件都是基于ace-builds或者brace包装的 不如直接用下面的&#xff0c;不如直接使用下面的 <template><div ref"editor" class"json-editor"><…...

threejs——无人机概念切割效果

主要技术采用着色器的切割渲染,和之前写的风车可视化的文章不同,这次的切割效果是在着色器的基础上实现的,并新增了很多可调节的变量,兄弟们,走曲儿~ 线上演示地址,点击体验 源码下载地址,点击下载 正文 从图中大概可以看出以下信息,一个由线组成的无人机模型,一个由…...

360极速浏览器不支持看PDF

360安全浏览器采用的是基于IE内核和Chrome内核的双核浏览器。360极速浏览器是源自Chromium开源项目的浏览器&#xff0c;不但完美融合了IE内核引擎&#xff0c;而且实现了双核引擎的无缝切换。因此在速度上&#xff0c;360极速浏览器的极速体验感更佳。 展示自己的时候要在有优…...

Python Turtle 实现动态时钟:十二时辰与星空流星效果

在这篇文章中&#xff0c;我将带你通过 Python 的 turtle 模块构建一个动态可视化时钟程序。这个时钟不仅具备传统的时间显示功能&#xff0c;还融合了中国古代的十二时辰与八卦符号&#xff0c;并通过动态星空、流星效果与昼夜背景切换&#xff0c;为程序增添了观赏性和文化内…...

使用 UniApp 实现简单的个人中心页面

1. 创建 UniApp 项目 首先&#xff0c;确保你已经安装了 HBuilderX 或其他支持 UniApp 的开发工具。然后创建一个新的 UniApp 项目。 # 使用 HBuilderX 创建新项目 # 选择 uni-app 模板 -> 选择 Vue.js 模板 -> 输入项目名称 -> 创建2. 安装依赖 UniApp 内置了一些…...

spring cloud contract http实例

微服务很多时&#xff0c;服务之前相互调用&#xff0c;接口参数的一致性要变得很难维护。 spring cloud contract 提供了测试接口一致性的方法。 一 项目配置 plugins {id "groovy"id "org.springframework.cloud.contract" version "4.0.5"i…...

修改docker源

在/etc/docker/daemon.json文件中写入 { "registry-mirrors": [ "Welcome to nginx!" ] } 执行 systemctl daemon-reload systemctl restart docker docker info能够看到源已经被替换 现在国内能够使用的docker源经过测试只有Welcome to nginx! …...

顺序表的使用,对数据的增删改查

主函数&#xff1a; 3.c #include "3.h"//头文件调用 SqlListptr sql_cerate()//创建顺序表函数 {SqlListptr ptr(SqlListptr)malloc(sizeof(SqlList));//在堆区申请连续的空间if(NULLptr){printf("创建失败\n");return NULL;//如果没有申请成功&#xff…...

【在Linux世界中追寻伟大的One Piece】自旋锁

目录 1 -> 概述 2 -> 原理 3 -> 优缺点及使用场景 3.1 -> 优点 3.2 -> 缺点 3.3 -> 使用场景 4 -> 纯软件自旋锁类似的原理实现 4.1 -> 结论 5 -> 样例代码 1 -> 概述 自旋锁是一种多线程同步机制&#xff0c;用于保护共享资源避免受并…...

信创改造-Spring Boot 项目部署至 TongWeb

打 war 包参考&#xff1a;https://blog.csdn.net/z1353095373/article/details/144330999...

选择WordPress和Shopify:搭建对谷歌SEO友好的网站

在建设网站时&#xff0c;不仅要考虑它的美观和功能性&#xff0c;还要关注它是否对谷歌SEO友好。如果你希望网站能够获得更好的搜索排名&#xff0c;WordPress和Shopify是两个值得推荐的建站平台。 WordPress作为最流行的内容管理系统&#xff0c;其强大的灵活性和丰富的插件…...

Rust之抽空学习系列(二)—— 编程通用概念(上)

Rust之抽空学习系列&#xff08;二&#xff09;—— 编程通用概念&#xff08;上&#xff09; Rust 作为一门强类型的静态类型语言&#xff0c;会有哪些与生俱来的天赋呢&#xff1f; 1、静态类型语言 vs 动态类型语言 特性静态动态类型检查编译时运行时错误检测执行前执行期…...

《神经网络与深度学习》(邱锡鹏) 内容概要【不含数学推导】

第1章 绪论 基本概念&#xff1a;介绍了人工智能的发展历程及不同阶段的特点&#xff0c;如符号主义、连接主义、行为主义等。还阐述了深度学习在人工智能领域的重要地位和发展现状&#xff0c;以及其在图像、语音、自然语言处理等多个领域的成功应用。术语解释 人工智能&…...

Wireshark如何查看数据包时间间隔

1.如果数据包量不大&#xff0c;抓包本身也不大&#xff0c;建议从绝对时间判断&#xff0c;打开wireshark软件&#xff0c;并点开相应要分析的抓包文件。 进入到最上方菜单<视图>,在弹出菜单选择时间显示格式&#xff0c;再在右侧菜单中选择自捕获经过的秒数。 这样就可…...

ISP和IQ调试(一)

系列文章目录 文章目录 系列文章目录前言一、ISP&#xff08;image signal process)二、ISP位置三、IQ总结 前言 一、ISP&#xff08;image signal process) image signal process 图像处理技术 image signal processor 图像信号处理器 设备 什么是图像信号&#xff1f; 代表…...

uniapp改成用vue起项目

目的&#xff1a;让项目按照vue的打包流程跑流水线 1.按照uniapp官网教程执行 2.执行第二条命令时报错 ERROR Failed to get response from true/vue-cli-version-marker 3.解决方式 报错可能跟yarn有关&#xff0c;然后切换成npm 找到自己本地电脑的这个文件 按照截图修…...

java之集合(详细-Map,Set,List)

1集合体系概述 1.1集合的概念 集合是一种容器&#xff0c;用来装数据的&#xff0c;类似于数组&#xff0c;但集合的大小可变&#xff0c;开发中也非常常用。 1.2集合分类 集合分为单列集合和多列集合 Collection代表单列集合&#xff0c;每个元素&#xff08;数据&#xff…...

利用卷积神经网络进行手写数字的识别

数据集介绍 MNIST&#xff08;Modified National Institute of Standards and Technology&#xff09;数据集是一个广泛使用的手写数字识别数据集&#xff0c;常用于机器学习和计算机视觉领域中的分类任务。它包含了从0到9的手写数字样本&#xff0c;常用于训练和测试各种图像…...

Flutter 桌面端串口配置

前言 我使用flutter_libserialport包在macOS中实现串口通信的功能&#xff0c;可以实现数据收发&#xff0c;但是收到的内容是乱码。这种情况一般都是由于波特率和硬件设备不一致导致的。 配置串口配置 1.打开串口读写 import package:flutter_libserialport/flutter_libser…...

Java 的常量池与 String 优化

Java 中的常量池&#xff08;Constant Pool&#xff09;是一种内存优化机制&#xff0c;比如字符串常量池&#xff1a; String s1 "Hello"; String s2 "Hello"; System.out.println(s1 s2); // 输出 true&#xff0c;因为指向同一池中的对象但对于使用…...

防范TCP攻击:策略与实践

TCP&#xff08;传输控制协议&#xff09;是互联网通信的核心协议之一&#xff0c;它确保了数据在网络上的可靠传输。然而&#xff0c;TCP也容易成为各种网络攻击的目标&#xff0c;如SYN洪水攻击、TCP连接耗尽攻击等。本文将探讨如何通过配置防火墙规则、优化服务器设置以及采…...

单片机:实现呼吸灯(附带源码)

单片机实现呼吸灯详细解读 呼吸灯是一种常见的灯光效果&#xff0c;广泛应用于电子产品、汽车、家居照明等领域。其基本特性是通过逐渐增亮和减弱的方式&#xff0c;使得灯光呈现出“呼吸”的效果&#xff0c;给人一种平缓、舒适的视觉感受。在嵌入式系统中&#xff0c;呼吸灯…...