在c++中使用字面量,yes /nay

Use of Literals, yay/nay in C++

本文关键字:yes nay c++      更新时间:2023-10-16

我最近听说,在某些情况下,程序员认为永远不应该在代码中使用字面量。我理解,在某些情况下,将变量名分配给给定的数字可能会有所帮助(特别是在维护方面,如果该数字在其他地方使用)。但是,请考虑以下案例研究:

案例研究1:对"特殊"字节码使用字面值

假设您有一个if语句,用于检查存储在uint16_t中的特定值(为了参数的缘故)。下面是两个代码示例:

版本1:

// Descriptive comment as to why I'm using 0xBEEF goes here
if (my_var == 0xBEEF) {
  //do something
}
版本2:

const uint16_t kSuperDescriptiveVarName = 0xBEEF;
if (my_var  == kSuperDescriptiveVarName) {
  // do something
}

就良好的编码实践而言,哪一种是"首选"方法?如果不止一次使用kSuperDescriptiveVarName,我完全可以理解为什么您更喜欢版本2。此外,编译器是否做了任何优化,使两个版本有效地相同的可执行代码?也就是说,这对性能有什么影响吗?

案例研究2:sizeof的使用

我完全理解,为了可移植性和可读性考虑,使用sizeof比使用原始文字更可取。考虑这两个代码示例。该场景是,您正在计算到数据包缓冲区的偏移量(uint8_t数组),其中数据包的第一部分存储为my_packet_header,我们说它是uint32_t

版本1:

const int offset = sizeof(my_packet_header);
版本2:

const int offset = 4;  // good comment telling reader where 4 came from

显然,版本1是首选,但是对于需要跳过多个数据字段的情况呢?如果您使用以下内容:

版本1:

const int offset = sizeof(my_packet_header) + sizeof(data_field1) + sizeof(data_field2) + ... + sizeof(data_fieldn);
版本2:

const int offset = 47;

在这种情况下哪个是首选的?显示计算偏移量所涉及的所有步骤是否仍然有意义,或者这里的文字使用是否有意义?

在我尝试改进我的代码实践时,提前感谢您的帮助。

就良好的编码实践而言,哪一种是"首选"方法?如果kSuperDescriptiveVarName不止一次使用,我完全可以理解为什么你更喜欢版本2。

听起来你明白要点了…分解在多个地方使用的值(及其注释)。此外,有时将一组常量放在一个地方会有所帮助——这样它们的值就可以被检查、验证、修改等,而不必关心它们在代码中的使用位置。其他时候,有许多常数在附近使用,需要正确解释它们的注释会混淆使用它们的代码。

与此相反,拥有const变量意味着所有研究代码的程序员都想知道它是否在其他地方使用过,在检查声明它的作用域的其余部分时要记住它,等等——记住的不必要的东西越少,对代码重要部分的理解就越准确。

就像编程中的许多事情一样,平衡每种方法的优缺点是一门"艺术",最好由经验和对代码可能被研究、维护和发展的方式的知识来指导。

同样,编译器是否做了任何优化,使两个版本有效地相同的可执行代码?也就是说,这对性能有什么影响吗?

优化后的代码没有性能影响。

我完全理解,为了可移植性和可读性考虑,使用sizeof比使用原始文字更可取。

还有其他原因。良好编程的一个重要因素是在完成更改后减少维护点。如果您可以修改变量的类型,并且知道使用该变量的所有地方都会相应地进行调整,那就太棒了——节省了时间和潜在的错误。使用sizeof可以帮助解决这个问题。

哪一个是首选[计算偏移量的结构]?显示计算偏移量所涉及的所有步骤是否仍然有意义,或者这里的文字使用是否有意义?

offsetof宏(#include <cstddef>)更适合此…再次减少维护负担。使用this + that方法,如果编译器决定使用任何填充,您的偏移量将是错误的,并且您必须在每次添加或删除字段时修复它。

忽略这些问题,只考虑你的this + that的例子作为一个更复杂的值分配的说明,这也是一个平衡的行为。你肯定想要一些解释/注释/文档(你是在计算早期字段的二进制大小吗?)计算下一个字段的偏移量?故意遗漏一些可能不需要用于预期用途的字段,或者这是偶然的?…)。不过,一个命名常量可能就足够了,所以选择哪一种方式可能并不重要....

在你列出的每一个例子中,我都会用名字。

在您的第一个示例中,您几乎肯定至少两次使用了特殊的0xBEEF数字—一次用于编写它,一次用于进行比较。如果你没有写它,这个数字仍然是与他人签订的合同的一部分(可能是文件格式定义)。

在最后一个示例中,显示产生该值的计算特别有用。这样,如果你遇到麻烦,你可以很容易地看到数字是可信的,或者你错过了什么,并修复它。

在某些情况下,我更喜欢字面量而不是命名常量。在这些情况下,名称并不比数字更有意义。例如,你有一个玩骰子游戏(可能是Yahtzee)的游戏程序,其中有针对特定骰子投掷的特定规则。您可以为One = 1, Two = 2等定义常量。但为什么要这么麻烦呢?

一般来说,最好使用名称而不是值。毕竟,如果您稍后需要更改它,您可以更容易地找到它。另外,当你阅读代码时,并不总是清楚为什么要使用这个特定的数字,所以给它分配一个有意义的名称,会让程序员立即明白这一点。

性能方面没有区别,因为优化器应该处理它。即使生成了一条额外的指令,这也不太可能给你带来麻烦。如果您的代码非常紧凑,那么无论如何都不应该依赖优化器的效果。

我完全可以理解为什么你更喜欢版本2,如果kSuperDescriptiveVarName被使用不止一次。

我认为kSuperDescriptiveVarName肯定会被使用不止一次。一个用于检查,至少一个用于赋值,可能在程序的不同部分。

在性能上没有区别,因为在几乎所有的编译器中都存在一个名为Constant Propagation的优化。