使用'const'作为功能参数

时间:2020-03-06 14:33:43  来源:igfitidea点击:

你用const可以走多远?我们是在必要时只是使函数const还是全力以赴,并在各处使用它?例如,假设有一个简单的变量,它带有一个布尔值:

void SetValue(const bool b) { my_val_ = b; }

那个const实际上有用吗?我个人选择广泛使用它,包括参数,但是在这种情况下,我想知道它是否值得?

让我惊讶的是得知我们可以从函数声明中的参数中省略const,但可以将其包含在函数定义中,例如:

.h文件

void func(int n, long l);

.cpp文件

void func(const int n, const long l)

是否有一个原因?对我来说似乎有点不寻常。

解决方案

原因是参数的const仅在函数内本地应用,因为它正在处理数据的副本。这意味着函数签名实际上是相同的。经常这样做可能是不好的风格。

我个人倾向于不使用const,除了引用和指针参数。对于复制的对象,这并不重要,尽管它可以更安全,因为它表示函数中的意图。这确实是一个判断电话。我确实倾向于使用const_iterator,尽管在循环执行某些操作时,并且我不打算对其进行修改,因此,只要严格维护引用类型的const正确性,我就对每个人都猜想。

我在作为引用(或者指针)的函数参数上使用const,这些参数仅是[输入]数据,不会被函数修改。意思是,当使用引用的目的是避免复制数据并且不允许更改传递的参数时。

在示例中,将const放在boolean b参数上只会对实现施加约束,并且不会对类的接口有所帮助(尽管通常建议不要更改参数)。

的功能签名

void foo(int a);

void foo(const int a);

一样,这说明了.c和.h

阿萨夫

在我们提到的情况下,它不会影响API的调用者,这就是为什么它不常用(并且在标头中没有必要)的原因。它仅影响功能的实现。

这样做并不是一件坏事,但是考虑到它不会影响API并带来输入的好处,它带来的好处并不是那么大,因此通常不会这样做。

我不会像每个人都已经知道布尔值(而不是boolean&)是常量那样在参数上加上const,因此将其添加将使人们认为"等等,是什么?"甚至我们是通过引用传递参数。

使用const时要记住的是,从一开始就使const变得容易得多,而不是以后尝试将它们放入。

如果我们希望某些内容保持不变,请使用const,它提供了一个提示,该提示描述了函数的功能以及预期的功能。我看过很多可以使用其中一些的C API,尤其是那些接受C字符串的C API!

与标题相比,我更倾向于在cpp文件中省略const关键字,但是由于我倾向于剪切+粘贴它们,因此将它们保留在两个位置。我不知道为什么编译器允许这样做,我想这是一个编译器。最佳实践肯定是将const关键字放入两个文件中。

啊,一个艰难的人。一方面,声明是一个契约,通过值传递const参数确实没有任何意义。另一方面,如果我们看一下函数的实现,则在声明参数常量的情况下,可以给编译器更多的优化机会。

示例中的所有const都没有目的。 C ++默认情况下是按值传递的,因此该函数获取这些int和boolean的副本。即使函数确实修改了它们,调用者的副本也不会受到影响。

所以我会避免使用额外的const,因为

  • 他们是多余的
  • 他们弄乱了文字
  • 在可能有用或者有效的情况下,它们使我无法更改传递的值。

确实没有理由将值参数设为" const",因为该函数始终只能修改变量的副本。

使用" const"的原因是,如果我们要通过引用传递更大的内容(例如,具有很多成员的结构),则在这种情况下,它可以确保函数无法对其进行修改;或者更确切地说,如果我们尝试以常规方式进行修改,编译器将抱怨。这样可以防止意外修改它。

我可以使用const。参数常量意味着它们不应更改其值。当通过引用传递时,这尤其有价值。函数的const声明该函数不应更改类成员。

我不对值传递的参数使用const。调用者不在乎是否修改参数,这是实现细节。

真正重要的是,如果方法不修改其实例,则将其标记为const。随便执行此操作,因为否则可能会导致很多const_cast <>结束,或者可能会发现标记方法const需要更改大量代码,因为它调用了应该标记为const的其他方法。

如果不需要修改它们,我也倾向于标记本地vars const。我相信通过使识别"运动部件"更容易使代码更易于理解。

仅当参数通过引用(即引用或者指针)传递时,const参数才有用。当编译器看到const参数时,请确保未在函数体内修改该参数中使用的变量。为什么有人要将按值参数设置为常量? :-)

如果参数是通过值(而不是引用)传递的,则通常将参数是否声明为const都没有太大区别(除非它包含引用成员-内置类型不是问题)。如果参数是引用或者指针,通常最好保护引用/指向的内存,而不是指针本身(我认为我们不能使引用本身为const,这并不重要,因为我们不能更改引用者) 。
保护作为const的所有内容似乎是一个好主意。如果参数只是POD(包括内置类型),并且没有沿途进一步更改的可能性(例如,在示例中为bool参数),则可以省略它而不必担心会犯错误。

我不知道.h / .cpp文件声明的区别,但这确实有些道理。在机器代码级别,没有什么是" const",因此,如果将一个函数(在.h中)声明为非const,则该代码与将其声明为const一样(不进行优化)。但是,它可以使编译器登记为我们不会在函数(.ccp)的实现内更改变量的值。当我们从允许更改的接口继承时,它可能会派上用场,但无需更改参数即可实现所需的功能。

我倾向于在任何可能的地方使用const。 (或者目标语言的其他适当关键字。)我之所以这样做,纯粹是因为它允许编译器进行额外的优化,否则它将无法进行其他优化。由于我不知道这些优化可能是什么,所以即使在看起来很愚蠢的地方,我也总是这样做。

就我所知,编译器很可能会看到一个const值参数,并说:"嘿,此函数无论如何都不会对其进行修改,因此我可以按引用传递并节省一些时钟周期。"我认为它永远不会做这样的事情,因为它改变了函数签名,但是它说明了这一点。也许它执行了一些不同的堆栈操作或者其他操作……要点是,我不知道,但是我知道尝试变得比编译器更聪明只会导致我感到羞耻。

C ++具有一些额外的负担,带有const-correctness的概念,因此它变得更加重要。

以下两行在功能上等效:

int foo (int a);
int foo (const int a);

显然,如果以第二种方式定义了foo主体,则将无法对其进行修改,但是与外部没有区别。

真正方便使用const的地方是引用或者指针参数:

int foo (const BigStruct &a);
int foo (const BigStruct *a);

这说明foo可以在不复制的情况下采用较大的参数,可能是大小为千兆字节的数据结构。而且,它对调用方说:" Foo不会*更改该参数的内容。"传递const引用还允许编译器做出某些性能决定。

*:除非它消除了常量性,但这是另一篇文章。

当参数通过值传递时,const是没有意义的,因为我们将不会修改调用者的对象。

通过引用传递时,最好使用const,除非该函数的目的是修改传递的值。

最后,一个不修改当前对象(此对象)的函数可以并且可能应该声明为const。下面是一个示例:

int SomeClass::GetValue() const {return m_internalValue;}

这是保证不会修改此调用所应用到的对象。换句话说,我们可以致电:

const SomeClass* pSomeClass;
pSomeClass->GetValue();

如果该函数不是const,则将导致编译器警告。

"当参数通过值传递时,const是没有意义的,因为我们将不会修改调用者的对象。"

错误的。

这是关于自我记录代码和假设的过程。

如果代码中有很多人在使用它,而函数却很简单,那么我们应该在所有可能的地方标上" const"。在编写具有行业实力的代码时,我们应该始终假设同事是精神病患者,他们想尽一切可能(特别是因为将来通常是我们自己)。

此外,正如前面提到的那样,它可能会帮助编译器稍微优化一些东西(尽管这是一个漫长的过程)。

有时(太频繁了!)我必须解开别人的C ++代码。而且我们都知道,别人的C ++代码几乎完全是按定义定义的:)因此,解密本地数据流的第一件事是将const放入每个变量定义中,直到编译器开始吠叫为止。这也意味着const限定值参数,因为它们只是调用方初始化的漂亮局部变量。

啊,我希望默认情况下变量是const的,而非const变量则需要可变的:)

当我用C ++编码谋生时,我便尽了一切可能。使用const是帮助编译器帮助好方法。例如,构造方法的返回值可以使我们免于输入错误,例如:

foo() = 42

当你的意思是:

foo() == 42

如果定义了foo()以返回非常量引用:

int& foo() { /* ... */ }

编译器将很高兴让我们为函数调用返回的匿名临时变量分配一个值。使它成为常量:

const int& foo() { /* ... */ }

消除这种可能性。

将值参数标记为" const"绝对是主观的。

但是,实际上,我更喜欢标记值参数const,就像示例一样。

void func(const int n, const long l) { /* ... */ }

对我而言,该值清楚地表明该功能从未更改该功能参数值。它们在开始时和结束时将具有相同的值。对我来说,这是保持一种非常实用的编程风格的一部分。

对于一个简短的函数,将" const"放在其中可能是浪费时间/空间,因为通常很明显参数没有被函数修改。

但是,对于较大的功能,它是一种实现文档形式,并且由编译器强制执行。

我可以确定,如果我用'n'和'l'进行一些计算,则可以重构/移动该计算而不必担心会得到不同的结果,因为我错过了一个或者两个都被更改的地方。

由于它是实现的详细信息,因此我们无需在标头中声明值参数const,就像我们不需要使用实现所用的名称来声明函数参数一样。

由于参数是通过值传递的,因此从调用函数的角度来看,是否指定const不会有任何区别。将值传递参数声明为const基本上没有任何意义。

const应该是C ++中的默认值。
像这样 :

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable

我说const是值参数。

考虑以下越野车功能:

bool isZero(int number)
{
  if (number = 0)  // whoops, should be number == 0
    return true;
  else
    return false;
}

如果number参数为const,则编译器将停止并警告我们该错误。

在comp.lang.c ++。mode中的旧"每周专家"文章中对此主题进行了很好的讨论。

相应的GOTW文章可在Herb Sutter的网站上找到。

关于编译器优化:http://www.gotw.ca/gotw/081.htm