使用'const'作为函数参数

Rob*_*Rob 368 c++ const

你走多远const?您是否只是const在必要时制作功能,或者您是否全力以赴并在任何地方使用它?例如,想象一个简单的mutator,它接受一个布尔参数:

void SetValue(const bool b) { my_val_ = b; }
Run Code Online (Sandbox Code Playgroud)

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

我还惊讶地发现你可以省略const函数声明中的参数,但可以将它包含在函数定义中,例如:

.h文件

void func(int n, long l);
Run Code Online (Sandbox Code Playgroud)

.cpp文件

void func(const int n, const long l)
Run Code Online (Sandbox Code Playgroud)

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

rle*_*lut 395

"当参数按值传递时,const是没有意义的,因为你不会修改调用者的对象."

错误.

它是关于自我记录您的代码和您的假设.

如果你的代码中有很多人正在处理它并且你的功能是非常重要的,那么你应该将"const"标记为任何内容以及你能做到的一切.在编写工业级代码时,你应该总是假设你的同事是精神病患者试图以任何方式找到你(特别是因为它通常是你自己的未来).

此外,正如之前提到的那样,它可能有助于编译器稍微优化一些事情(虽然这是一个很长的镜头).

  • 并且它可以捕获函数内部的错误* - 如果你知道不应该更改参数,那么声明它是const意味着编译器将告诉你是否意外修改它. (54认同)
  • 完全同意.所有这些都与人们进行沟通,并限制变量可以做什么. (37认同)
  • 我已经投了这个.声明参数'const'会将参数添加到语义信息中.它们突出了代码的原始作者的意图,这将有助于随着时间的推移维护代码. (24认同)
  • 我已经投了这个.当你将它应用于简单的值传递参数时,我认为你稀释了你想用const表示的东西. (17认同)
  • @tonylo:你误解了.这是关于将一​​个局部变量标记为代码块内的const(恰好是一个函数).我会对任何局部变量做同样的事情.它与具有const-correct的API是正交的,这确实也很重要. (13认同)
  • "你应该总是假设你的同事是精神病患者试图以任何方式帮助你"让我在工作中大笑.为此+1. (7认同)
  • 但它限制了你对算法的选择(或强迫你做出不必要的复制) - 如果我称你的罪(x)我不在乎你的sin()算法是否应该在评估答案时改变它的X副本. (5认同)
  • 我认为使用带有非引用/指针参数的 const 与带括号的 return 语句相同。 (5认同)
  • 不是真的,我认为你只是高估了const的用处.当对象的所有权对于定义很重要时,我认为它有用.看看POSIX API,open(2),例如路径名(指针)值得制作const,对于标志和模式它是无关紧要的. (3认同)
  • @litb:在需要时编写const永远不会错! (3认同)
  • 我同意你应该尽可能多地使用const,但const不应该在头文件中,因为接口的用户对方法/函数体内部所做的事情不感兴趣,参数传递给值. (3认同)
  • 这次讨论中缺少的是接口契约和实现之间的区别.在方法声明中将const添加到by-value参数不会改变接口的语义,因此不能向客户端发送任何信号 - constness不是合同的一部分.将其包含在定义中是有用的,向未来的实施者发出信号,表明它不打算在实现中进行更改.在定义中没有const的声明中可以使用const.接口中的by-value const是不必要的混乱. (3认同)
  • @tonylo,我对这个问题保持中立态度.如果人们把const放在参数中,那就好了.但我不会在我的代码中这样做.但是提及"打开"等手册并不是一个真正的争论.因为`const`不是接口的一部分.因此,在联机帮助页中显示`const`是错误的. (2认同)
  • “Reductio ad absurdum” - 答案是尽可能使用“const”。这只会导致 API 混乱,难以阅读并限制算法。`void bytecopy(char * CONST dest, const char * CONST source, CONST int count) { while(count--) { *dest++=*source++; } }` 不起作用,四个常量中的三个(大写的)是不必要的。 (2认同)
  • 如果你的同事是精神病患者,期望他们写`struct Foo {void bar(const string); }; void Foo :: bar(string s){s ="我修改了一个应该是const的字符串"; 对自我记录这么多. (2认同)
  • -1 对于 Oktalist 所说的,有精神病患者,然后有常态精神病患者。通常他们是同一个精神分裂症患者。如果您曾经在 const 正确的代码中的任何地方只将 const 丢弃一次,那么所有关于自记录代码的假设都会崩溃,因为您无法再信任 API。 (2认同)

Gre*_*ers 172

原因是参数的const仅在函数中本地应用,因为它正在处理数据的副本.这意味着函数签名实际上是相同的.尽管这样做很可能很糟糕.

我个人倾向于不使用const,除了参考和指针参数.对于复制的对象,它并不重要,尽管它可以更安全,因为它在函数内表示意图.这真是一个判断电话.我确实倾向于使用const_iterator但是在循环某些东西时我并不打算修改它,所以我猜他自己的每一个,只要严格保持引用类型的const正确性.

  • 我不能同意"坏风格"部分.从函数原型中删除`const`的优点是,如果您决定稍后从实现部分删除`const`,则不需要更改头文件. (52认同)
  • "除了参考和指针参数外,我个人倾向于不使用const." 也许你应该澄清一下"我倾向于不在函数声明中使用多余的限定符,而是使用`const`来实现它有用的区别." (4认同)
  • `int getDouble(int a){++ a; 返回2*a; 试试吧.当然,`++ a`与此无关,但它可以在很长一段时间内由多个程序员编写的长函数中找到.我强烈建议编写`int getDouble(const int a){// ...}`,当找到`++ a;`时会产生编译错误. (4认同)
  • 我不同意这个答案.我瘦的其他方式和标记参数`const`尽可能; 它更具表现力.当我阅读别人的代码时,我会使用这样的小指标来判断他们在编写代码时需要多少关注,包括魔术数字,评论和正确的指针使用等. (3认同)
  • 这完全取决于谁需要哪些信息.您可以通过值**提供参数**,这样调用者**就不需要知道任何事情**就您(内部)使用它做什么.所以写`class Foo {int multiply(int a,int b)const; 在你的标题中.在你的实现中**你要小心**你可以保证不改变`a`和`b`所以`int Foo :: multiply(const int a,const int b)const {}`在这里有意义.(旁注:调用者和实现都关心函数不会改变它的`Foo`对象,因此声明结束时的const) (3认同)

Con*_*tin 148

有时(经常!)我必须解开其他人的C++代码.而且我们都知道其他人的 C++代码几乎是按照定义完全混乱:)所以我做的第一件事就是解密本地数据流,将const放在每个变量定义中,直到编译器开始吠叫.这也意味着const限定值参数,因为它们只是由调用者初始化的奇特局部变量.

嗯,我希望变量是常量默认和可变被要求对非const变量:)

  • Const-antin ......我看到你父母在那里做了什么 (203认同)
  • 默认情况下,[Rust](https://www.rust-lang.org/)中的变量是"`const`":) (7认同)
  • @ysap,1.尽可能标记const让我看到哪些部分在移动,哪些部分不在.根据我的经验,许多当地人实际上是const,而不是相反.2."Const变量"/"不可变变量"可能听起来像矛盾,但是在函数式语言中是标准实践,也是一些非功能性语言; 请参阅Rust例如:https://doc.rust-lang.org/book/variable-bindings.html (6认同)
  • “const”antin当然会这么说!抱歉,我很惊讶没有人这么说...... (5认同)
  • "我希望变量默认为常量" - 一个矛盾吗?8-)说真的,"压缩"一切如何帮助你解开代码?如果原作者改变了一个假设不变的论证,你怎么知道var应该是一个常数呢?此外,绝大多数(非参数)变量都是......变量.所以编译器应该在你启动进程后很快就会中断,不是吗? (3认同)
  • 在 C++ 的某些情况下,现在也是标准的;例如,lambda `[x](){return ++x;}` 是一个错误;见[这里](/sf/ask/385137161/) (2认同)

Ben*_*aub 78

以下两行在功能上是等效的:

int foo (int a);
int foo (const int a);
Run Code Online (Sandbox Code Playgroud)

显然a,foo如果它是第二种方式定义的话,你将无法在体内进行修改,但与外界没有区别.

const真正派上用场是引用或指针参数:

int foo (const BigStruct &a);
int foo (const BigStruct *a);
Run Code Online (Sandbox Code Playgroud)

这就是foo可以采用一个大参数,也许是一个大小为千兆字节的数据结构,而不是复制它.此外,它告诉调用者,"Foo不会*改变该参数的内容." 传递const引用还允许编译器做出某些性能决策.

*:除非它抛弃了常数,但这是另一篇文章.

  • 这不是这个问题。当然,对于引用或指向的参数,最好使用const(如果未修改引用或指向的值)。注意,在指针示例中,不是* parameter *才是const;参数所指向的就是它。 (2认同)

Adi*_*sak 66

从API观点来看,额外多余的const是不好的:

在代码中为函数传递的内部类型参数添加额外的多余const 会使您的API混乱,同时对调用者或API用户没有任何有意义的承诺(它只会妨碍实现).

API中太多'const'在不需要的时候就像" 狼来了 ",最终人们会开始忽略'const',因为它遍布整个地方并且在大多数时候都没有意义.

API中额外consts的"reductio ad absurdum"参数对于前两个点是有好处的,如果更多的const参数是好的,那么每个可以有const的参数,应该在它上面有一个const.事实上,如果它真的那么好,你会希望const成为参数的默认值,并且只有当你想要改变参数时才有一个像"mutable"这样的关键字.

因此,让我们尽可能地尝试使用const:

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);
Run Code Online (Sandbox Code Playgroud)

考虑上面的代码行.声明更加混乱,更长,更难阅读,但API用户可以安全地忽略四个'const'关键字中的三个.但是,'const'的额外使用使得第二行可能存在危险!

为什么?

快速误读第一个参数char * const buffer可能会让您认为它不会修改传入的数据缓冲区中的内存 - 但是,这不是真的!多余的"常量"可能会在扫描或快速误读时导致对API的危险和错误假设.


从代码实现的角度来看,多余的const也很糟糕:

#if FLEXIBLE_IMPLEMENTATION
       #define SUPERFLUOUS_CONST
#else
       #define SUPERFLUOUS_CONST             const
#endif

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count);
Run Code Online (Sandbox Code Playgroud)

如果FLEXIBLE_IMPLEMENTATION不成立,则API"承诺"不以下面的第一种方式实现该功能.

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       // Will break if !FLEXIBLE_IMPLEMENTATION
       while(count--)
       {
              *dest++=*source++;
       }
}

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       for(int i=0;i<count;i++)
       {
              dest[i]=source[i];
       }
}
Run Code Online (Sandbox Code Playgroud)

这是一个非常愚蠢的承诺.你为什么要做出一个承诺,对你的来电者没有任何好处,只会限制你的实施?

这两个都是相同功能的完全有效的实现,但是你所做的就是不必要地将一只手绑在背后.

此外,这是一个非常浅薄的承诺,很容易(并在法律上规避).

inline void bytecopyWrapped(char * dest,
   const char *source, int count)
{
       while(count--)
       {
              *dest++=*source++;
       }
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source,SUPERFLUOUS_CONST int count)
{
    bytecopyWrapped(dest, source, count);
}
Run Code Online (Sandbox Code Playgroud)

看,我无论如何都以这种方式实现它,即使我承诺不 - 只是使用包装函数.这就像坏人承诺不会在电影中杀死某人并命令他的追随者杀死他们一样.

那些多余的const只不过是一个电影坏人的承诺.


但是撒谎的能力变得更糟:

我已经开悟了你可以通过使用虚假const来匹配头(声明)和代码(定义)中的const.const-happy倡导者声称这是一件好事,因为它允许你只将const放在定义中.

// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }
Run Code Online (Sandbox Code Playgroud)

但是,反过来也是如此......你只能在声明中放置一个虚假的const,并在定义中忽略它.这只会使API中多余的const变得更加可怕,而且是一个可怕的谎言 - 请参阅此示例:

class foo
{
    void test(int * const pi);
};

void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
    pi++;  // I promised in my definition I wouldn't modify this
}
Run Code Online (Sandbox Code Playgroud)

所有多余的const实际上都是通过强制他在想要更改变量或通过非const引用传递变量时强制使用另一个本地副本或包装函数来使实现者的代码可读性降低.

看看这个例子.哪个更具可读性?很明显,第二个函数中额外变量的唯一原因是因为某个API设计者投入了多余的const?

struct llist
{
    llist * next;
};

void walkllist(llist *plist)
{
    llist *pnext;
    while(plist)
    {
        pnext=plist->next;
        walk(plist);
        plist=pnext;    // This line wouldn't compile if plist was const
    }
}

void walkllist(llist * SUPERFLUOUS_CONST plist)
{
    llist * pnotconst=plist;
    llist *pnext;
    while(pnotconst)
    {
        pnext=pnotconst->next;
        walk(pnotconst);
        pnotconst=pnext;
    }
}
Run Code Online (Sandbox Code Playgroud)

希望我们在这里学到了一些东西.多余的const是一个混乱的API,一个恼人的唠叨,一个浅薄和毫无意义的承诺,一个不必要的障碍,偶尔会导致非常危险的错误.

  • @Adisak我认为你的答案本身没有任何问题,但从你的评论中可以看出你错过了一个重要的观点.函数定义/实现不是API的一部分,它只是函数*声明*.正如您所说,使用const参数声明函数是没有意义的,并且会增加混乱.但是API的用户可能永远不需要看到它的实现.同时,为了清楚起见,实现者可能决定仅对函数定义中的一些参数进行常量限定,这非常好. (20认同)
  • @ jw013是正确的,`void foo(int)`和`void foo(const int)`是完全相同的函数,而不是重载.ideone.com/npN4W4 ideone.com/tZav9R此处的const只是函数体的实现细节,对重载决策没有影响.如果您不打算修改复制的值,请将const保留在声明之外,以获得更安全和更整洁的API,但将const放入_definition_. (13认同)
  • 为什么选票呢?如果你对downvote做一个简短的评论会更有帮助. (7认同)
  • 使用const参数的重点是使标记行失败(plist = pnext).保持函数参数不可变是一种合理的安全措施.我同意你的观点,即它们在函数声明中很糟糕(因为它们是超级的),但是它们可以在实现块中用于它们的目的. (7认同)
  • @Adisak我知道这是旧的,但我相信公共API的正确用法将是另一种方式.这样开发人员在内部工作时就不会犯错误,例如`pi ++`. (3认同)
  • 没有理由将API混乱以暴露内部实现细节.很简单,使你的实现接受参数`T parm_foo`并立即分配给`const&T foo = parm_foo`然后在函数中使用`foo` - 你可以强制参数的恒定性,而不会给最终用户带来不必要的API混乱. (2认同)
  • "如果不需要API中的太多'const'就像"狼来了",最终人们会开始忽略'const',因为它遍布整个地方并且大部分时间都没有意义"什么?常量的一点是,它不能被忽略,因为编译器不会让你忽略它. (2认同)
  • 它肯定有助于捕捉人类容易产生的微妙错误,尤其是当代码库中const的使用是一致的时候.一个例如.将是`std :: string`的`c_str()`函数,它为底层字符串返回一个`const char*`.有人错误地修改了传递的字符串.编译器救了我们. (2认同)
  • @AbhinavGauniyal 对不应修改字符的字符串使用“const char *”是明智的。这是必要的常量。然而,API 中不必要的常量的一个例子是对同一个字符串使用“const char * const”,甚至更糟的是,对函数确实修改了字符的字符串使用“char * const”。 (2认同)
  • Downvoting因为这个答案的很大一部分似乎是关于你被误解的事情的咆哮(函数*定义*中的const参数不影响API).中途停下来你会承认这一点,但是如果你想编辑并删除最初的错误部分,或者在开头添加一个大的免责声明说第一部分是错误的,那将会有所帮助. (2认同)

QBz*_*ziZ 37

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

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable
Run Code Online (Sandbox Code Playgroud)

  • 与C的兼容性太重要了,至少对于设计C++的人来说,甚至考虑到这一点. (24认同)
  • 正是我的想法. (8认同)
  • 类似地,`unsigned`应该是C++中的默认值.像这样:`int i = 5; //我是unsigned`和`signed int i = 5; //我签了名. (6认同)
  • 有意思,我从未想过这一点. (4认同)

Avd*_*vdi 25

当我用C++编写代码时,我尽可能地把所有东西都拿走了.使用const是帮助编译器帮助您的好方法.例如,修改方法返回值可以避免输错,例如:

foo() = 42
Run Code Online (Sandbox Code Playgroud)

当你的意思是:

foo() == 42
Run Code Online (Sandbox Code Playgroud)

如果foo()被定义为返回非const引用:

int& foo() { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

编译器很乐意让你为函数调用返回的匿名临时值赋值.使它成为const:

const int& foo() { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

消除这种可能性.

  • 有什么编译器可以工作?GCC在尝试编译`foo()= 42`时出错:错误:左值作为赋值的左操作数需要左值 (6认同)
  • 我遇到过const的这种用法,我可以告诉你,最终它产生的方式比收益更麻烦.提示:`const int foo()`与`int foo()`的类型不同,如果你使用函数指针,信号/槽系统或boost :: bind等东西,会给你带来很大的麻烦. (2认同)
  • 我已经更正了代码以包含引用返回值. (2认同)

Voi*_*oid 15

有关于这一主题在comp.lang.c文章++老"本周的大师"商量好了.主持这里.

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

  • 好文章,但我不同意他的说法。我也将它们设为const,因为它们就像变量一样,而且我永远不希望有人对我的参数进行任何更改。 (2认同)

小智 9

我说const你的值参数.

考虑这个错误的功能:

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

如果number参数是const,编译器将停止并警告我们这个bug.

  • 那太可怕了! (5认同)
  • @ChrisHuang-Leaver可怕它不是,如果像尤达一样说话你:http://stackoverflow.com/a/2430307/210916 (5认同)
  • 另一种方法是使用if(0 == number)... else ...; (2认同)

Gab*_*les 9

1.根据我的评估最佳答案:

根据我的评估,@Adisak的答案是这里的最佳答案。请注意,这个答案是一部分最好的,因为它也是最知名的备份与真正的代码示例除了使用声音和深思熟虑出来的逻辑。

2.我自己的话(同意最佳答案):

  1. 对于按值传递,添加const. 它所做的只是:
    1. 限制实现者每次想要更改源代码中的输入参数时都必须制作一个副本(该更改无论如何都不会产生副作用,因为传入的内容已经是一个副本,因为它是按值传递的)。通常,更改按值传递的输入参数用于实现该功能,因此const随处添加会阻碍这一点。
    2. const不必要地添加const到处都是 s的代码,从而将注意力从const真正需要安全代码的s 上移开。
  2. 然而,在处理指针引用时const在需要时非常重要,并且必须使用,因为它可以防止在函数持续更改带来的不良副作用,因此当参数仅作为输入时,每个指针或引用都必须使用const,不是输出。const 在通过引用或指针传递的参数上使用具有额外的好处,可以使哪些参数是指针或引用变得非常明显。突出并说“小心!const旁边的任何参数都是引用或指针!”是另一回事。
  3. 我上面描述的内容经常是我工作过的专业软件组织达成的共识,并被认为是最佳实践。有时甚至,规则是严格的:“永远不要在按值传递的参数上使用 const,但如果它们只是输入,则始终在通过引用或指针传递的参数上使用它。”

3.谷歌的话(同意我和最佳答案):

(来自“ Google C++ 风格指南”)

对于按值传递的函数参数,const 对调用者没有影响,因此不建议在函数声明中使用。参见TotW #109

既不鼓励也不鼓励在局部变量上使用 const。

来源:Google C++ 风格指南的“使用常量”部分:https : //google.github.io/styleguide/cppguide.html#Use_of_const。这实际上是一个非常有价值的部分,因此请阅读整个部分。

请注意,“TotW #109”代表“本周提示 #109:const在函数声明中有意义”,也是一个有用的读物​​。它提供的信息更丰富,对做什么的规定更少,并且基于上下文出现在上面引用的 Google C++ 样式指南规则之前const,但由于它提供的清晰性,const上面引用的规则被添加到 Google C++时尚指南。

另请注意,即使我在这里引用 Google C++ 样式指南来捍卫我的立场,但这并不意味着我总是遵循该指南或始终建议遵循该指南。他们推荐的一些东西很奇怪,比如他们kDaysInAWeek的 "Constant Names" 风格命名约定然而,当世界上最成功和最有影响力的技术和软件公司之一使用与我和@Adisak 等其他人相同的理由来支持我们在这个问题上的观点时,指出这一点仍然是有用和相关的。

4. Clang 的 linter, clang-tidy, 对此有一些选择:

A.这也是值得注意的是,铛的棉短绒,clang-tidy,有一个选项,readability-avoid-const-params-in-decls这里描述的,以支持在一个代码库执行使用const对于传值函数的参数

检查函数声明是否具有顶级常量的参数。

声明中的 const 值不会影响函数的签名,因此不应将它们放在那里。

例子:

void f(const string);   // Bad: const is top level.
void f(const string&);  // Good: const is not top level.
Run Code Online (Sandbox Code Playgroud)

为了完整性和清晰性,我还添加了两个示例:

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]
Run Code Online (Sandbox Code Playgroud)

B. 它也有这个选项:readability-const-return-type- https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

5. 我对如何撰写有关此事的风格指南的务实态度:

我只需将其复制并粘贴到我的风格指南中:

[复制/粘贴开始]

  1. const当它们的内容(它们指向的内容)不打算更改时,始终在通过引用或指针传递的函数参数上使用。这样,当通过引用或指针传递的变量预计会被更改时,很明显,因为它将缺少const. 在这个用例中,const可以防止函数之外的意外副作用。
  2. 这是不建议在使用const上按值传递函数的参数,因为const对来电者没有影响:即使变量是在函数变化会有以外的功能无副作用。有关其他理由和见解,请参阅以下资源:
    1. 《Google C++ Style Guide》《Const 的使用》一节
    2. “本周提示 #109:const在函数声明中有意义”
    3. Adisak 的 Stack Overflow 回答“使用‘const’作为函数参数”
  3. "永远不要不是定义的声明中的函数参数上使用顶级const[即:const按值传递的参数上] (并注意不要复制/粘贴无意义的)。它毫无意义并且被编译器忽略,它是视觉噪音,它可能会误导读者”(https://abseil.io/tips/109,重点补充)。const
  4. 唯一const对编译有影响的限定符是那些放置在函数定义中的限定符,而不是那些位于函数的前向声明中的限定符,例如头文件中的函数(方法)声明中的限定符。
  5. 永远不要在函数返回使用顶级const[即:const通过值传递的变量上] 。
  6. const在函数返回的指针或引用上使用取决于实现者,因为它有时很有用。
  7. TODO:使用以下clang-tidy选项强制执行上述某些操作:
  8. https://clang.llvm.org/extra/clang-tidy/checks/readability-avoid-const-params-in-decls.html
  9. https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

下面是一些代码示例来演示上述const规则:

const参数示例:(
有些是从这里借来的)

void f(const std::string);   // Bad: const is top level.
void f(const std::string&);  // Good: const is not top level.

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]
Run Code Online (Sandbox Code Playgroud)

const返回类型示例:(
有些是从这里借用的)

// BAD--do not do this:
const int foo();
const Clazz foo();
Clazz *const foo();

// OK--up to the implementer:
const int* foo();
const int& foo();
const Clazz* foo();
Run Code Online (Sandbox Code Playgroud)

[复制/粘贴结束]

关键词:constin 函数参数的使用;编码标准;C和C++编码标准;编码指南;最佳实践;代码标准;const 返回值


Asa*_*f R 7

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

在示例中将const放在boolean b参数上只会对实现施加约束,并且不会为类的接口做出贡献(尽管通常建议不更改参数).

功能签名

void foo(int a);
Run Code Online (Sandbox Code Playgroud)

void foo(const int a);
Run Code Online (Sandbox Code Playgroud)

是相同的,这解释了你的.c和.h

阿萨夫


Meh*_*dad 7

如果你使用->*.*运算符,这是必须的.

它会阻止你写一些像

void foo(Bar *p) { if (++p->*member > 0) { ... } }
Run Code Online (Sandbox Code Playgroud)

我现在几乎做了,而且可能不会做你想要的.

我打算说的是

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }
Run Code Online (Sandbox Code Playgroud)

如果我把const之间的Bar *p,编译器会告诉我的.

  • 当我要将许多运算符混合在一起时(如果我还不知道100%),我会立即检查运算符优先级的引用,所以IMO不是问题. (4认同)

Nem*_*vic 5

啊,一个艰难的.一方面,声明是一个契约,按值传递一个const参数实际上没有意义.另一方面,如果查看函数实现,如果声明参数常量,则会为编译器提供更多优化机会.


Dan*_*ett 5

当参数按值传递时, const 毫无意义,因为您不会修改调用者的对象。

当通过引用传递时,const 应该是首选,除非函数的目的是修改传递的值。

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

int SomeClass::GetValue() const {return m_internalValue;}
Run Code Online (Sandbox Code Playgroud)

这是一个不修改应用此调用的对象的承诺。换句话说,您可以调用:

const SomeClass* pSomeClass;
pSomeClass->GetValue();
Run Code Online (Sandbox Code Playgroud)

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


Llo*_*oyd 5

标记值参数'const'绝对是一个主观的东西.

但是我实际上更喜欢将值参数标记为const,就像在您的示例中一样.

void func(const int n, const long l) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

我的值是清楚地表明功能参数值永远不会被函数改变.它们在开头和结尾处具有相同的值.对我来说,这是保持一种非常实用的编程风格的一部分.

对于一个简短的函数来说,在那里使用'const'可能是浪费时间/空间,因为通常非常明显的是参数不会被函数修改.

但是对于更大的函数,它是一种实现文档形式,并由编译器强制执行.

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

由于它是一个实现细节,因此您不需要在头文件中声明值参数const,就像您不需要使用与实现使用的名称相同的名称来声明函数参数.