你用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形参仅在通过引用(即引用或指针)传递形参时才有用。当编译器看到一个const形参时,它会确保形参中使用的变量在函数体中没有被修改。为什么会有人想让一个按值参数作为常数呢?: -)

从API的角度来看,多余的const是不好的:

在代码中为通过value传递的内在类型参数添加多余的const会使API变得混乱,同时对调用者或API用户没有任何有意义的承诺(它只会阻碍实现)。

在一个不需要的API中有太多的“const”就像“狼来了”一样,最终人们会开始忽略“const”,因为它到处都是,大多数时候没有任何意义。

在API中,将"推理还原"参数转化为额外的const参数对前两点来说是好的,如果更多的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);

考虑上面的代码行。不仅声明更混乱、更长、更难阅读,而且API用户可以安全地忽略四个“const”关键字中的三个。然而,额外使用“const”使得第二行有潜在的危险!

Why?

对第一个参数char * const buffer的快速误读可能会使您认为它不会修改传入的数据缓冲区中的内存——然而,这不是真的!当快速扫描或误读时,多余的“const”可能会导致对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);

如果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];
       }
}

这是一个非常愚蠢的承诺。为什么要做出一个对调用者毫无好处、只会限制实现的承诺呢?

这两个都是同一个函数的完全有效实现,所以你所做的一切都是不必要的束手束脚。

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

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);
}

看,我无论如何都是这样实现的,尽管我承诺不会这样做——只是使用一个包装器函数。这就像电影里的坏人承诺不杀人,却命令他的追随者去杀了他们。

那些多余的const值不过是电影里坏人的一个承诺。


但撒谎的能力更糟:

我已经被启发,你可以不匹配的const头(声明)和代码(定义)使用虚假的const。喜欢使用const的人声称这是一件好事,因为它允许你只在定义中使用const。

// Example of const only in definition, not declaration
struct foo { void test(int *pi); };
void foo::test(int * const pi) { }

然而,反之亦然。您可以只在声明中放入伪const,而在定义中忽略它。这只会让API中多余的const变得更糟糕,更像是一个可怕的谎言——请看下面的例子:

struct 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
}

多余的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;
    }
}

希望我们学到了一些东西。多余的const是一个让api混乱的眼中钉、一个烦人的唠叨、一个肤浅而无意义的承诺、一个不必要的障碍,并且偶尔会导致非常危险的错误。

下面两行在功能上是等价的:

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

显然,如果用第二种方式定义,就不能在foo对象体中修改a,但从外部看没有区别。

const真正有用的地方是引用或指针形参:

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

这就是说,foo可以接受一个大的参数,也许是一个千兆字节大小的数据结构,而不需要复制它。同时,它也告诉调用者:“Foo不会*改变参数的内容。”传递const引用还允许编译器做出某些性能决定。

*:除非它抛弃了const-ness,但那是另一篇文章。

May be this wont be a valid argument. but if we increment the value of a const variable inside a function compiler will give us an error: "error: increment of read-only parameter". so that means we can use const key word as a way to prevent accidentally modifying our variables inside functions(which we are not supposed to/read-only). so if we accidentally did it at the compile time compiler will let us know that. this is specially important if you are not the only one who is working on this project.

确实没有理由将值形参设为“const”,因为函数只能修改变量的副本。

使用“const”的原因是如果你通过引用传递更大的东西(例如有很多成员的结构体),在这种情况下,它确保函数不能修改它;或者更确切地说,如果您试图以常规方式修改它,编译器将报错。它可以防止它被意外修改。