你用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应该是c++的默认值。 像这样:

int i = 5 ; // i is a constant

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

从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混乱的眼中钉、一个烦人的唠叨、一个肤浅而无意义的承诺、一个不必要的障碍,并且偶尔会导致非常危险的错误。

我对函数形参使用const,这些形参是引用(或指针),只是[in]数据,不会被函数修改。意思是,使用引用的目的是避免复制数据,不允许更改传递的参数。

在你的例子中,将const放在布尔b形参上只是对实现施加了约束,对类的接口没有贡献(尽管通常建议不改变形参)。

的函数签名

void foo(int a);

and

void foo(const int a);

是一样的,这就解释了你的。c和。h

Asaf

当我以编写c++为生时,我尽可能地压缩了所有东西。使用const是一种帮助编译器帮助你的好方法。例如,const你的方法返回值可以避免你输入错误,例如:

foo() = 42

你的意思是:

foo() == 42

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

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

编译器很乐意让您为函数调用返回的匿名临时对象赋值。使其为const:

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

消除了这种可能性。

总结:

通常情况下,const的值传递是无用的,最多是误导。从GOTW006 但是您可以像处理变量一样在.cpp中添加它们。 注意,标准库不使用const。例如std::vector::at(size_type pos)。对标准库来说足够好的东西对我来说也是好的。