C++-常量参数和类变量是否令人讨厌

C++ - Are const parameters and class variables pessimization?

本文关键字:讨厌 是否 类变量 常量 参数 C++-      更新时间:2023-10-16

我正试图弄清楚在编写C++代码时什么时候应该使用const。这些都是令人讨厌的例子吗?或者这样写代码有益吗?:

示例1:

int findVal(const int OTHER_VAL) const
{
switch(OTHER_VAL)
{
case 1:
return 2;
default:
return 3;
}
}

示例2:

enum class MobType
{
COW, CHICKEN, DOG, PIG
};
class BaseMob
{
protected:
BaseMob(const MobType TYPE) : TYPE(TYPE) { }
const MobType TYPE;
};

示例3:

void showWorld(const World &world)
{
auto data = world.getData();
for (auto &i:data)
i.print();
}

不,它们不是。

带有自动存储的局部变量(包括函数args)上的const纯粹是语法糖,可以帮助程序员为代码设置规则。这对优化器没有任何帮助。优化编译器从C源中提取必要的数据移动,并对其进行优化。他们通常不关心您是否将同一个tmp变量用于许多不同的事情,或者在同一个函数中有10个不同的const tmp1 = a+10;

是的,这适用于通过值传递的函数args;它们是具有自动存储的局部变量,在寄存器或堆栈中传递。不,这并不意味着调用者可以假设函数没有修改用于传递arg的堆栈内存,所以它对优化器也没有太大帮助。(使用相同的参数进行第二次函数调用仍然需要将参数重新写入堆栈(如果不是所有的参数都适合寄存器),因为参数上的const不会改变这样一个事实,即被调用的函数"拥有"该堆栈空间,并且可以随心所欲地将其用作暂存空间。)

静态/全局/引用变量上的

const确实有帮助。static const int foo = 10;可以作为立即常量内联,而不是从内存加载。(例如add eax, 10而不是add eax, [foo])。


使用const将类方法标记为不更改任何类成员也可以帮助编译器避免在函数调用后重新加载类成员。(即将其保存在寄存器中)。这通常只适用于编译器看不到函数定义的情况,否则一个好的优化编译器只能查看被调用函数的作用并进行相应的优化。(只要它不在Unix库中,符号插入意味着它不能假设在编译时看到的被调用函数是动态链接后调用的函数。)

只要在逻辑上不更改值或对象,就应该将其设为const。从逻辑上讲,我并不是指每次在技术上都允许,而是指每次在函数、类和代码的上下文中都是合乎逻辑的。

一个简单的示例可以是示例1中的一个简单"get"函数,这些函数不应该修改类的状态,因此应该标记为常量,因为这将有助于向用户记录您的意图,此外还有助于确保类的不变性。

在某些情况下,创建一个不可变对象是有意义的,如示例2中所示。我们在C++中并不经常看到这些,但许多其他语言经常使用它们。如果它没有添加任何值来在对象生存期内更改某个成员,那么您不妨将其设为常量。

传递const-reference参数可以为您提供引用的性能优势,但同时确保源对象保持不变,这对用户来说既是一个很好的文档,也允许进行som优化。

在提到了所有这些原因之后,还有其他使用const的原因,正如在最后一段中简要提到的,即优化。当编译器知道某个东西是常量并且没有被更改时,它可以启用一些非常聪明的优化,但出于性能原因,不要使用const

这也是为什么通过(例如)const_cast强制转换来处理constness(可以丢弃const)可能会导致一些不期望的行为。例如,检查以下内容:

#include <stdio.h>
static const int foo = 10;
int constsum(void) {
return foo + 5;
}
int main(int argc, char* argv[]) {
int a = constsum();
int* newFoo = const_cast<int*>(&foo);
*newFoo = 20;
int b = constsum();
printf("%dn", a + b);
return 0;
}

从这个例子中可以看出(请参阅此处运行的代码),这可能不会产生所需的结果,因为30中的代码结果正在打印,而可能不会像预期的那样40。

当检查生产的程序集时,我们可以看到为什么(编译成程序集):

constsum():
mov     eax, 15
ret
main:
mov     eax, 30
ret

编译器只是内联这些值,因为它可以看到它们是常量,所以它不需要特别注意const_cast的使用。

因此,常量正确性和const的使用是一个有价值的工具,它可以提高代码的性能和稳定性,但也有助于(不要忘记)记录代码。