C++编程:数字运算、鼠标交互及常见问题处理
立即解锁
发布时间: 2025-08-20 01:48:51 阅读量: 2 订阅数: 3 


懒惰程序员的C++入门指南
### C++ 编程:数字运算、鼠标交互及常见问题处理
#### 1. 常量声明
在 C++ 中,对于那些不能改变的值,使用常量声明是个好主意。例如,对于圆周率 PI 这样的值,需要保证其不变性。对于整数类型,有一种方便的常量声明方式——使用枚举(enum)。
```cpp
//Location and dimensions for a box we might draw
enum {BOX_LEFT=100, BOX_TOP=50, BOX_WIDTH=200, BOX_HEIGHT=100};
```
这种方式比分别使用 `const int` 声明更简洁:
```cpp
const int BOX_LEFT =100;
const int BOX_TOP = 50;
const int BOX_WIDTH =200;
const int BOX_HEIGHT=100;
```
使用常量而非字面量值有两个主要原因:
- **清晰性**:在程序中看到一个字面量值,如 7,很难立刻知道它代表什么含义,可能是一周的天数、七宗罪的数量或者其他含义。使用常量可以通过清晰的名称来明确其含义。
- **可维护性**:如果后续发现使用的数字有误,使用常量只需修改一处,而使用字面量则需要在整个程序中查找并修改,工作量大。
常量声明的黄金法则是:只要一个数字字面量的用途不明显,就将其定义为一个全大写的常量符号,并在引用时使用该名称。
#### 2. 数学运算符
在 C++ 中,无论是使用 `float`、`double` 类型还是 `int` 类型,都可以使用以下算术运算符:
| 运算符 | 含义 |
| ---- | ---- |
| + | 加法 |
| - | 减法、取反 |
| * | 乘法 |
| / | 除法 |
| % | 取模 |
这些运算符的使用方式与预期一致,例如 `2.6+0.4`、`alpha/beta` 或 `-2*(5+3)`。
#### 3. 整数除法
在 C++ 中,整数除法的结果是整数。例如,`5/2` 的结果是 2,而不是 2.5。这是因为两个操作数都是整数,所以进行的是整数除法。取模运算符 `%` 用于获取除法的余数,例如 `5%2` 的结果是 1。
#### 4. 赋值运算符
赋值运算符 `=` 用于给变量赋值。常量一旦赋值就不能再改变,而变量可以随时改变其值。例如:
```cpp
const SSDL_Color MAHOGANY = SSDL_CreateColor(192, 64, 0);
int seasonsOfAmericanIdol = 13;
x = 5; y = 10;
x = 10;
seasonsOfAmericanIdol = seasonsOfAmericanIdol + 1;
```
还可以使用复合赋值运算符,如 `+=`、`-=`、`*=`、`/=` 和 `%=`。例如,`seasonsOfAmericanIdol += 1` 与 `seasonsOfAmericanIdol = seasonsOfAmericanIdol + 1` 含义相同。
#### 5. 跳水板示例
下面是一个使用枚举和数学运算符的程序示例,用于展示跳水运动员的路径:
```cpp
//Program to draw the path of a diver
// -- from _C++ for Lazy Programmers_
#include "SSDL.h"
int main(int argc, char** argv)
{
SSDL_SetWindowTitle("Sploosh! Hit a key to end");
//Stuff about the board
enum {BOARD_WIDTH = 60, BOARD_THICKNESS = 8, BOARD_INIT_Y =20};
SSDL_RenderDrawRect(0, BOARD_INIT_Y,
BOARD_WIDTH, BOARD_THICKNESS);
//...the water
enum { SKY_HEIGHT = 440 };
SSDL_SetRenderDrawColor(BLUE);
SSDL_RenderFillRect(0, SKY_HEIGHT,
SSDL_GetWindowWidth(),
SSDL_GetWindowHeight() - SKY_HEIGHT);
//...the diver
enum {WIDTH = 10, HEIGHT = 10};//Dimensions of the "diver"
enum {DISTANCE_TO_TRAVEL = 20};//How far to go right each time
enum {FACTOR_TO_INCREASE = 2};//Increase Y this much each time
enum { INIT_X = 50, INIT_Y = 10 };
int x = INIT_X; //Move diver to end of board
int y = INIT_Y; //and just on top of it
const SSDL_Color DIVER_COLOR = SSDL_CreateColor(200, 150, 90);
SSDL_SetRenderDrawColor(DIVER_COLOR);
//Now draw several images, going down as if falling, and right
//Remember x+=DISTANCE_TO_TRAVEL means x=x+DISTANCE_TO_TRAVEL
// ...and so on
SSDL_RenderFillRect(x, y, WIDTH, HEIGHT);
x += DISTANCE_TO_TRAVEL; //go right the same amount each time,
y *= FACTOR_TO_INCREASE; // down by an ever-increasing amount
//Same thing repeated several times
SSDL_RenderFillRect(x, y, WIDTH, HEIGHT);
x += DISTANCE_TO_TRAVEL; y *= FACTOR_TO_INCREASE;
SSDL_RenderFillRect(x, y, WIDTH, HEIGHT);
x += DISTANCE_TO_TRAVEL; y *= FACTOR_TO_INCREASE;
SSDL_RenderFillRect(x, y, WIDTH, HEIGHT);
x += DISTANCE_TO_TRAVEL; y *= FACTOR_TO_INCREASE;
SSDL_RenderFillRect(x, y, WIDTH, HEIGHT);
x += DISTANCE_TO_TRAVEL; y *= FACTOR_TO_INCREASE;
SSDL_RenderFillRect(x, y, WIDTH, HEIGHT);
x += DISTANCE_TO_TRAVEL; y *= FACTOR_TO_INCREASE;
//end program
SSDL_WaitKey();
return 0;
}
```
这个程序有几个需要注意的地方:
- 所有变量都进行了初始化。
- 在任何计算或变量初始化中都没有使用裸数字字面量,全部使用了常量。
- 相同的两行代码重复了六次,后续会有更好的实现方式。
#### 6. 数学运算符的“无忧”列表
C++ 在处理数学运算符时,有一些特性无需特别记忆:
- **优先级**:与代数课一样,乘法和除法的优先级高于加法和减法。如果需要改变运算顺序,可以使用括号。例如,`2*5+3` 的结果是 `(2*5)+3 = 13`,而不是 `2*(5+3) = 16`。
- **结合性**:算术运算从左到右进行,赋值运算从右到左进行。例如,`27/3/3` 会先计算 `(27/3)/3`,而 `x=5+2` 会先计算 `5+2` 的值,再将结果赋给 `x`。
- **强制类型转换**:如果需要将一个类型的变量转换为另一个类型,C++ 会自动进行转换。例如,`double Nothing = 0;` 会将 `Nothing` 初始化为 0.0,而 `int Something = 2.7;` 会将 `Something` 初始化为 2,因为整数不能有小数部分。如果在计算中混合使用整数和浮点数,结果将是浮点数。
#### 7. 内置函数和类型转换
在 C++ 中,可以使用 `cmath` 头文件中的数学函数,如 `sin` 和 `cos`。下面是一个使用这些函数绘制五角星的程序示例:
```cpp
//Program to make a 5-point star in center of screen
// -- from _C++ for Lazy Programmers_
#include <cmath>
#include "SSDL.h"
int main(int argc, char** argv)
{
const double PI = 3.14159;
//Starting out with some generally useful numbers...
const int CENTER_X=SSDL_GetWindowWidth() /2; //center of screen
const int CENTER_Y=SSDL_GetWindowHeight()/2;
enum { RADIUS = 200 };
enum { NUMBER_OF_POINTS = 5 };
//angle information...
double angle = 0; //angle starts at 0
const double ANGLE_INCREMENT = (2 / NUMBER_OF_POINTS) * PI;
//...now we make the successive lines
int x, y; //endpt of line (other endpt is center)
x = CENTER_X + int(RADIUS * cos(angle)); //calc endpoint
y = CENTER_Y + int(RADIUS * sin(angle));
SSDL_RenderDrawLine(CENTER_X, CENTER_Y, x, y); //draw line
angle += ANGLE_INCREMENT; //go on to next
x = CENTER_X + int(RADIUS * cos(angle)); //calc endpoint
y = CENTER_Y + int(RADIUS * sin(angle));
SSDL_RenderDrawLine(CENTER_X, CENTER_Y, x, y); //draw line
angle += ANGLE_INCREMENT; //go on to next
x = CENTER_X + int(RADIUS * cos(angle)); //calc endpoint
y = CENTER_Y + int(RADIUS * sin(angle));
SSDL_RenderDrawLine(CENTER_X, CENTER_Y, x, y); //draw line
angle += ANGLE_INCREMENT; //go on to next
x = CENTER_X + int(RADIUS * cos(angle)); //calc endpoint
y = CENTER_Y + int(RADIUS * sin(angle));
SSDL_RenderDrawLine(CENTER_X, CENTER_Y, x, y); //draw line
angle += ANGLE_INCREMENT; //go on to next
x = CENTER_X + int(RADIUS * cos(angle)); //calc endpoint
y = CENTER_Y + int(RADIUS * sin(angle));
SSDL_RenderDrawLine(CENTER_X, CENTER_Y, x, y); //draw line
angle += ANGLE_INCREMENT; //go on to next
//end program
SSDL_WaitKey();
return 0;
}
```
在这个程序中,最初的 `ANGLE_INCREMENT` 计算结果为 0,因为 `2` 和 `NUMBER_OF_POINTS` 都是整数,进行的是整数除法。需要使用类型转换来解决这个问题,例如:
```cpp
const double ANGLE_INCREMENT=(2/double(NUMBER_OF_POINTS))*PI;
```
#### 8. 常见问题及解决方法
- **调用有返回值的函数但未使用结果**:如果调用了一个有返回值的函数,但没有使用其返回值,C++ 不会报错,但这会浪费时间。例如:
```cpp
SSDL_GetScreenWidth();
SSDL_RenderTextCentered (320, 240, "Blastoff!");
```
应该使用返回值,例如:
```cpp
SSDL_RenderTextCentered(SSDL_GetScreenWidth ()/2,
SSDL_GetScreenHeight()/2,
"Blastoff!");
```
或者将返回值存储在变量或常量中:
```cpp
const int SCREEN_WIDTH = SSDL_GetScreenWidth ();
const int SCREEN_HEIGHT = SSDL_GetScreenHeight();
SSDL_RenderTextCentered (SCREEN_WIDTH/2, SCREEN_HEIGHT/2,
"Blastoff!");
```
- **整数除法得到错误结果**:如果希望得到浮点数结果,需要将其中一个操作数转换为浮点数或双精度数。例如,将 `2/NUMBER_OF_POINTS` 改为 `double(2)/NUMBER_OF_POINTS` 或 `2/double(NUMBER_OF_POINTS)` 或 `2.0 / NUMBER_OF_POINTS`。
- **类型转换警告**:如果收到类型转换警告,可以通过类型转换来消除警告,让编译器知道这是有意为之。
#### 9. 鼠标函数
可以使用 `SSDL_GetMouseX`、`SSDL_GetMouseY` 和 `SSDL_WaitMouse` 函数来获取鼠标的位置和等待鼠标点击。下面是一个示例程序:
```cpp
//Program to get a mouse click, and report its location
// -- from _C++ for Lazy Programmers_
#include "SSDL.h"
int main(int argc, char** argv)
{
sout << "Click the mouse and we'll see where you clicked.\n";
//Get the mouse click
SSDL_WaitMouse(); //wait for click...
int xLocation = SSDL_GetMouseX(),
yLocation = SSDL_GetMouseY(); //and get its X, Y location
//Print the mouse click
sout << "The X position of your click was " << xLocation << "\n";
sout << "The Y position of your click was " << yLocation << "\n";
//End the program
sout << "\n\nHit a key to end the program.\n";
SSDL_WaitKey();
return 0;
}
```
这些鼠标函数的原型如下:
| 函数 | 描述 |
| ---- | ---- |
| int SSDL_GetMouseX(); | 返回鼠标指针的 X 位置 |
| int SSDL_GetMouseY(); | 返回鼠标指针的 Y 位置 |
| void SSDL_WaitMouse(); | 等待任何鼠标按钮被点击 |
#### 10. 变量声明位置
变量声明的位置有两种常见方式:一种是在函数开头声明所有变量,另一种是在需要使用变量时再声明。“尽可能晚地声明变量”似乎更符合“偷懒”的原则,因为这样可以减少注释的需求,并且更容易找到变量的使用位置。
综上所述,C++ 提供了丰富的功能来处理数字运算、鼠标交互等问题。在编程过程中,需要注意常量的使用、运算符的优先级和类型转换等细节,以避免出现错误。同时,合理使用内置函数和类型转换可以提高代码的效率和可读性。
### C++ 编程:数字运算、鼠标交互及常见问题处理(续)
#### 11. 练习与拓展
在掌握了前面的 C++ 编程知识后,我们可以通过一些练习来巩固和拓展技能。
- **练习 1:调整五角星方向**
对于之前绘制五角星的程序,需要调整使其顶部的点垂直向上。可以通过调整起始角度来实现这一目标。我们知道五角星每个点之间的角度间隔是 `2π/5` 弧度,要让顶部点垂直向上,需要将起始角度设置为合适的值。例如,我们可以将起始角度设置为 `-π/2` 弧度(因为在数学坐标系中,垂直向上对应的角度是 `-π/2`)。修改后的代码如下:
```cpp
#include <cmath>
#include "SSDL.h"
int main(int argc, char** argv)
{
const double PI = 3.14159;
const int CENTER_X = SSDL_GetWindowWidth() / 2;
const int CENTER_Y = SSDL_GetWindowHeight() / 2;
enum { RADIUS = 200 };
enum { NUMBER_OF_POINTS = 5 };
double angle = -PI / 2; // 调整起始角度
const double ANGLE_INCREMENT = (2.0 / NUMBER_OF_POINTS) * PI;
int x, y;
for (int i = 0; i < NUMBER_OF_POINTS; ++i)
{
x = CENTER_X + int(RADIUS * cos(angle));
y = CENTER_Y + int(RADIUS * sin(angle));
SSDL_RenderDrawLine(CENTER_X, CENTER_Y, x, y);
angle += ANGLE_INCREMENT;
}
SSDL_WaitKey();
return 0;
}
```
- **练习 2:获取系统时间并转换格式**
要获取系统时间并将其转换为小时、分钟和秒的格式,可以使用 `<ctime>` 头文件中的 `time` 函数。以下是实现代码:
```cpp
#include <ctime>
#include <iostream>
int main()
{
int timeInSeconds = static_cast<int>(time(nullptr));
int hours = timeInSeconds / 3600;
int minutes = (timeInSeconds % 3600) / 60;
int seconds = timeInSeconds % 60;
std::cout << "Current time: " << hours << ":" << minutes << ":" << seconds << std::endl;
return 0;
}
```
这里的操作步骤如下:
1. 包含 `<ctime>` 头文件,以便使用 `time` 函数。
2. 调用 `time(nullptr)` 获取当前系统时间的秒数,并将其强制转换为 `int` 类型。
3. 使用 `/` 和 `%` 运算符将总秒数转换为小时、分钟和秒。
4. 输出转换后的时间。
- **练习 3:制作时钟表盘(较难)**
制作时钟表盘需要绘制一个圆,并在合适的位置标注数字 1 - 12。可以使用三角函数来计算每个数字的位置。以下是一个简单的实现思路:
```cpp
#include <cmath>
#include "SSDL.h"
const double PI = 3.14159;
int main(int argc, char** argv)
{
const int CENTER_X = SSDL_GetWindowWidth() / 2;
const int CENTER_Y = SSDL_GetWindowHeight() / 2;
const int RADIUS = 200;
// 绘制圆
for (int i = 0; i < 360; ++i)
{
double angle = i * PI / 180;
int x = CENTER_X + int(RADIUS * cos(angle));
int y = CENTER_Y + int(RADIUS * sin(angle));
SSDL_RenderDrawPoint(x, y);
}
// 标注数字 1 - 12
for (int i = 1; i <= 12; ++i)
{
double angle = (i * 30 - 90) * PI / 180; // 每个数字间隔 30 度,起始角度 -90 度
int x = CENTER_X + int((RADIUS - 20) * cos(angle));
int y = CENTER_Y + int((RADIUS - 20) * sin(angle));
SSDL_RenderTextCentered(x, y, std::to_string(i).c_str());
}
SSDL_WaitKey();
return 0;
}
```
实现步骤如下:
1. 计算时钟表盘的圆心位置 `CENTER_X` 和 `CENTER_Y` 以及半径 `RADIUS`。
2. 使用循环绘制圆,通过三角函数计算圆上每个点的坐标。
3. 再次使用循环标注数字 1 - 12,根据每个数字的角度间隔(30 度)和起始角度(-90 度)计算数字的位置。
4. 使用 `SSDL_RenderTextCentered` 函数在计算好的位置上显示数字。
#### 12. 总结与注意事项
在 C++ 编程中,我们需要注意以下几个方面:
- **常量的使用**:使用常量可以提高代码的可读性和可维护性。尽量避免使用裸数字字面量,而是将其定义为常量并使用常量名称。
- **运算符的优先级和结合性**:要清楚运算符的优先级和结合性,必要时使用括号来明确运算顺序。例如,在进行复杂的数学运算时,使用括号可以避免因优先级问题导致的错误。
- **类型转换**:在进行除法运算时,如果需要得到浮点数结果,要注意进行类型转换。同时,对于类型转换警告,要根据情况进行处理,确保代码的正确性。
- **函数的使用**:调用有返回值的函数时,要确保使用其返回值,避免浪费资源。
以下是一个简单的流程图,展示了在编写 C++ 程序时的基本注意事项:
```mermaid
graph LR
A[开始编程] --> B{使用常量?}
B -- 是 --> C[定义常量并使用]
B -- 否 --> D[可能降低代码可读性和可维护性]
C --> E{注意运算符优先级?}
E -- 是 --> F[正确使用运算符和括号]
E -- 否 --> G[可能导致运算结果错误]
F --> H{进行类型转换?}
H -- 是 --> I[正确处理类型转换]
H -- 否 --> J[可能得到错误的结果]
I --> K{使用函数返回值?}
K -- 是 --> L[有效利用函数结果]
K -- 否 --> M[浪费资源]
D --> N[重新审视代码]
G --> N
J --> N
M --> N
N --> A
```
通过不断练习和总结经验,我们可以更好地掌握 C++ 编程,编写出高效、可靠的代码。希望这些知识和练习能够帮助你在 C++ 编程的道路上不断前进。
0
0
复制全文
相关推荐










