在编程中,选择使用 switch-case
还是 if-else
语句通常取决于代码的可读性、维护性和特定的使用场景。对于它们的性能,以下是对它们的效率比较:
1. 基本原理:
-
if-else
语句:if-else
是一个链式结构,条件会一个一个被评估,直到找到第一个为真的条件为止。- 每个条件的计算都是线性的,最坏情况下,需要检查所有的条件。
-
switch-case
语句:switch-case
是一个多路分支结构,通常用于处理多个可能的值(通常是整数或枚举)。- 它会根据给定的表达式直接跳转到相应的分支,不必像
if-else
那样逐一检查条件。 - 在大多数情况下,编译器可以将
switch-case
优化为跳转表或哈希表,使得分支选择几乎是常数时间复杂度。
2. 性能分析:
-
编译器优化:
-
switch-case
:- 当分支数目较少时,编译器通常将其实现为一系列的条件检查(类似于
if-else
)。 - 当分支数目较多并且值是连续的(或几乎连续的),编译器可能会生成一个跳转表或哈希表,从而使得分支选择是常数时间复杂度
O(1)
。 - 这种优化在处理大量可能的值时非常有效。
- 当分支数目较少时,编译器通常将其实现为一系列的条件检查(类似于
-
if-else
:if-else
语句在编译器上通常被编译为一系列的条件跳转指令。- 如果条件是简单的布尔表达式,性能差别不大。
- 如果条件是复杂的表达式或者包含多个条件,性能会受到评估顺序和表达式复杂性的影响。
-
-
具体情境下的性能:
- 小范围分支:
- 当分支数目较少时(例如,2-3 个分支),
if-else
和switch-case
的性能差别通常可以忽略不计。 - 编译器在这种情况下可能会对两者做类似的优化。
- 当分支数目较少时(例如,2-3 个分支),
- 大范围分支:
- 对于多于几种可能性的分支,
switch-case
通常会更高效,尤其是在可以优化为跳转表的情况下。 - 在处理大型枚举或整数范围时,
switch-case
通常会有显著的性能优势。
- 对于多于几种可能性的分支,
- 小范围分支:
3. 代码可读性和维护性:
-
if-else
:if-else
适合于处理多种不同类型的条件(不仅限于整数值)。- 更灵活,可以处理任意复杂的逻辑。
- 可读性和代码结构在面对简单的条件时可能更清晰。
-
switch-case
:switch-case
在处理多个固定值时更直观。- 对于处理固定值集的逻辑(例如状态机、命令模式),
switch-case
更具可读性和结构性。
4. 总结:
-
性能:
- 在处理少量分支时,两者差异不大。
- 在处理大量固定值的分支时,
switch-case
通常更高效。
-
代码结构:
if-else
适合处理复杂的条件和逻辑。switch-case
适合处理大量固定值的情况,并且代码更简洁、可读。
5.执行效率
如果if判断有很多条语句判断,如何提高执行效率。如下例子,提高执行效率可以这样改写。
std::string DataLoadULog::reNamePlot(std::string name)
{
if(name=="vehicle_attitude_setpoint/pitch_body")
{
std::string plotname="6.姿态角度/俯仰角_期望";
return plotname;
}
if(name=="vehicle_attitude_setpoint/roll_body")
{
std::string plotname="6.姿态角度/滚转角_期望";
return plotname;
}
if(name=="vehicle_attitude_setpoint/yaw_body")
{
std::string plotname="6.姿态角度/偏航角_期望";
return plotname;
}
//7.姿态角速度
if(name=="vehicle_rates_setpoint/pitch")
{
std::string plotname="7.姿态角速度/俯仰角速度_期望";
return plotname;
}
if(name=="vehicle_rates_setpoint/roll")
{
std::string plotname="7.姿态角速度/滚转角速度_期望";
return plotname;
}
if(name=="vehicle_rates_setpoint/yaw")
{
std::string plotname="7.姿态角速度/偏航角速度_期望";
return plotname;
}
//4.高度信息
if(name=="sensor_gps/alt")
{
std::string plotname="4.高度信息/海拔高度(GNSS高度)";
return plotname;
}
if(name=="trajectory_setpoint/z")
{
std::string plotname="4.高度信息/融合高度_期望";
return plotname;
}
if(name=="vehicle_local_position/z")
{
std::string plotname="4.高度信息/融合高度_测量";
return plotname;
}
if(name=="vehicle_air_data/baro_alt_meter")
{
std::string plotname="4.高度信息/气压高度";
return plotname;
}
if(name=="distance_sensor/current_distance")
{
std::string plotname="4.高度信息/雷达高度";
return plotname;
}}
在 DataLoadULog::reNamePlot
函数中,使用了一系列的 if-else
语句来匹配输入的 name
字符串并返回对应的字符串。这种方法在分支数目较多的情况下,可能会影响性能。要提高效率,我们可以考虑以下优化方法:
使用 std::unordered_map
使用 std::unordered_map
来存储键值对,这样可以利用哈希表来实现常数时间复杂度的查找。std::unordered_map
是 C++ 标准库提供的哈希表实现,非常适合这种键值匹配的场景。
以下是如何使用 std::unordered_map
重写 reNamePlot
函数:
#include <unordered_map>
#include <string>class DataLoadULog {
public:std::string reNamePlot(const std::string& name);private:static const std::unordered_map<std::string, std::string> plotNameMap;
};// 定义并初始化 map
const std::unordered_map<std::string, std::string> DataLoadULog::plotNameMap = {{"vehicle_attitude_setpoint/pitch_body", "6.姿态角度/俯仰角_期望"},{"vehicle_attitude_setpoint/roll_body", "6.姿态角度/滚转角_期望"},{"vehicle_attitude_setpoint/yaw_body", "6.姿态角度/偏航角_期望"},{"vehicle_rates_setpoint/pitch", "7.姿态角速度/俯仰角速度_期望"},{"vehicle_rates_setpoint/roll", "7.姿态角速度/滚转角速度_期望"},{"vehicle_rates_setpoint/yaw", "7.姿态角速度/偏航角速度_期望"},{"sensor_gps/alt", "4.高度信息/海拔高度(GNSS高度)"},{"trajectory_setpoint/z", "4.高度信息/融合高度_期望"},{"vehicle_local_position/z", "4.高度信息/融合高度_测量"},{"vehicle_air_data/baro_alt_meter", "4.高度信息/气压高度"},{"distance_sensor/current_distance", "4.高度信息/雷达高度"}
};// 函数实现
std::string DataLoadULog::reNamePlot(const std::string& name) {auto it = plotNameMap.find(name);if (it != plotNameMap.end()) {return it->second;}return ""; // 或者返回一个默认值
}
优点:
- 效率高:查找时间复杂度为 O(1)。
- 代码简洁:清晰地将输入与输出的映射分开,容易添加新的映射。
- 易于维护:如果需要增加或修改映射,只需在
unordered_map
中添加或修改条目。