如何远做你去 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正确性进行严格维护。

其他提示

<!> quot;当参数按值传递时,const是没有意义的,因为你不会修改调用者的对象。<!> quot;

错误。

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

如果您的代码中有很多人在处理它并且您的功能非常重要,那么您应该标记<!> quot; const <!> quot;任何你想做的一切。在编写工业强度代码时,你应该总是假设你的同事是精神病患者试图以任何方式帮助你(特别是因为它通常是你自己的未来)。

此外,正如前面提到的那样,可能可以帮助编译器稍微优化一下(虽然这是一个很长的镜头)。

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

啊,我希望变量默认为 const ,非const变量需要可变:)

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

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

显然,如果第二种方式定义了a,你将无法在foo的主体中修改const,但与外界没有区别。

<=>真正派上用场的是参考或指针参数:

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

这就是foo可以采用一个大参数,也许是一个大小为千兆字节的数据结构,而不是复制它。此外,它对调用者说,<!>; Foo不会*改变该参数的内容。<!> quot;传递const引用也允许编译器做出某些性能决定。

*:除非它抛弃了常数,但那是另一个帖子。

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

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

API中太多'const'在不需要时就像<!>“哭狼 <!>”;最终人们会开始忽略'const',因为它遍布整个地方并且在大多数时候都没有任何意义。

<!> quot; reductio ad absurdum <!> quot; API中额外consts的参数对于前两个点是有好处的,如果更多的const参数是好的,那么每个可以有一个const的参数,应该在它上面有一个const。事实上,如果真的那么好,你需要const作为参数的默认值,并且有一个关键字,如<!> quot; mutable <!> quot;仅当您想要更改参数时。

因此,让我们尝试使用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'使第二行可能危险!

为什么?

快速误读第一个参数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 <!>#8220;承诺<!>#8221;不要在下面第一种方式实现这个功能。

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

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

这两个都是相同功能的完全有效的实现,但是你所做的所有<!>#8217;完成后不必要地将一只手绑在背后。

此外,<!>#8217是一个非常浅薄的承诺,很容易(并且在法律上被规避)。

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

看,我无论如何都是这样实现的,尽管我承诺不会<!>#8211;只是使用包装函数。它就像那个坏人承诺不会在电影中杀死某个人而命令他的追随者杀死他们一样。

那些多余的const <!>#8217; s的价值不过是电影坏人的承诺。


但撒谎的能力变得更糟:

我已经开悟过你可以通过使用虚假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) { }

然而,反之亦然......你只能在声明中放入一个虚假的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
}

所有多余的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应该是C ++中的默认值。 像这样:

int i = 5 ; // i is a constant

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

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

foo() = 42

当你的意思是:

foo() == 42

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

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

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

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

消除这种可能性。

在旧的<!>“本周的大师<!>”中对这个主题进行了很好的讨论。关于comp.lang.c ++的文章。主持此处

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

我说const你的值参数。

考虑这个错误的功能:

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

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

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

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

的功能签名
void foo(int a);

void foo(const int a);

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

阿萨夫

如果您使用->*.*运算符,则必须使用

它会阻止您编写类似

的内容
void foo(Bar *p) { if (++p->*member > 0) { ... } }

我现在几乎做了,而且可能不会按你的意愿行事。

我打算说的是

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }

如果我在constBar *之间放了一个p,那么编译器就会告诉我。

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

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

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

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

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

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

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

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

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

当参数按值传递时,

const是没有意义的,因为你不会修改调用者的对象。

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

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

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

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

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

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

可能这不是一个有效的论据。但是如果我们在函数编译器中增加一个const变量的值会给我们一个错误:  <!>“错误:只读参数的增量 <!>”;这意味着我们可以使用const关键字来防止意外修改函数内部的变量(我们不应该/只读)。因此,如果我们在编译时意外地完成了它,编译器就会告诉我们。如果您不是唯一一个正在从事这个项目的人,这一点非常重要。

在你提到的情况下,它不会影响你的API的调用者,这就是为什么它不常见(并且在标题中没有必要)。它只影响你的函数的实现。

这并不是特别糟糕的事情,但鉴于它不会影响您的API,并且它会增加打字,所以它的好处并不是那么好,所以通常不会这样做。

我不使用const作为传递值的参数。调用者不关心您是否修改参数,这是一个实现细节。

真正重要的是,如果方法不修改其实例,则将方法标记为const。你去的时候这样做,因为否则你可能会得到很多const_cast <!> lt; <!> gt;或者您可能会发现标记方法const需要更改大量代码,因为它调用了其他应该标记为const的方法。

如果我不需要修改它们,我也倾向于标记局部变量const。我相信通过更容易识别<!>“移动部件<!>”来使代码更容易理解。

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

就我所知,编译器可能会很好地看到一个const值参数,并说,<!>;嘿,这个函数无论如何都没有修改它,所以我可以通过引用传递并节省一些时钟周期。 <!> QUOT;我不认为它会做这样的事情,因为它改变了函数签名,但它说明了这一点。也许它会做一些不同的堆栈操作或者其他......重点是,我不知道,但我确实知道比编译器更聪明只会导致我感到羞耻。

C ++有一些额外的包袱,有了const-correctness的概念,所以它变得更加重要。

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

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

如果参数是按值传递的(并且不是引用),通常参数是否声明为const没有太大区别(除非它包含引用成员 - 对于内置类型不是问题)。如果参数是引用或指针,通常最好保护引用/指向内存,而不是指针本身(我认为你不能使引用本身为const,而不是因为你不能更改裁判而重要) 。 保护你能做的所有东西似乎是个好主意。你可以省略它,如果参数只是POD(包括内置类型)而不会出错,并且它们不可能在路上进一步改变(例如在你的例子中bool参数)。

我不知道.h / .cpp文件声明的区别,但它确实有道理。在机器代码级别,没有什么是<!>“const <!>”;所以如果你将一个函数(在.h中)声明为非const,代码就像你将它声明为const一样(优化除外)。但是,它可以帮助您登记编译器,您不会在函数(.ccp)的实现中更改变量的值。当您从允许更改的接口继承时,它可能会派上用场,但您不需要更改为参数来实现所需的功能。

以总结:

  • "常常量通过价值是无用的和误导性的。" 从 GOTW006
  • 但你可以把它们添加的。cpp,你会做的变量。
  • 注意,标准图书馆并没有使用常数。E.g。 std::vector::at(size_type pos).什么是好的足够标准图书馆是为我好。

我不会把const放在这样的参数上 - 每个人都已经知道布尔值(而不是布尔<!>放大器;)是常量,所以添加它会让人们想到<!>“等待,什么QUOT?<!>;甚至你通过引用传递参数。

使用const记住的事情是,从一开始就制作const更容易,而不是试着将它们放在后面。

当您希望某些内容保持不变时使用const - 它是一个添加的提示,用于描述您的功能和预期功能。我见过很多可以用它们中的一些的C API,尤其是接受c-strings的那些!

我更倾向于在cpp文件中省略const关键字而不是标题,但是因为我倾向于剪切+粘贴它们,所以它们会被保存在两个地方。我不知道编译器为什么允许这样,我猜它是编译器的东西。最佳做法是将const关键字放在两个文件中。

没有理由建立一个值参数<!> quot; const <!> quot;因为该函数无论如何都只能修改变量的副本。

使用<!>“const <!>”的原因;如果你通过引用传递更大的东西(例如一个有很多成员的结构),在这种情况下它确保函数不能修改它;或者更确切地说,如果您尝试以传统方式修改它,编译器会抱怨。它可以防止它被意外修改。

Const参数仅在参数通过引用传递时有用,即引用或指针。当编译器看到const参数时,它确保参数中使用的变量不在函数体内修改。为什么有人想要将by-value参数设为常量? : - )

当参数通过值传递时,如果从调用函数的角度指定const或不指定const,它没有任何区别。将值传递的值声明为const传递没有任何意义。

所有consts在您的例子有没有用处。C++是通过价值的默认,因此能得到的副本,这些整数和布尔.即使功能并对其进行修改,呼叫者的复制不受影响。

所以我们避免额外的consts因为

  • 他们redudant
  • 他们弄乱 文本
  • 他们阻止我 改变传递的价值 情况可能是有用或有效。

我知道问题是<!>“有点<!>”;过时了,但随着我的到来,其他人也可能会在将来这样做......我仍然怀疑这位可怜的家伙会在这里列出来阅读我的评论:)

在我看来,我们仍然过于局限于C风格的思维方式。在OOP范例中,我们使用对象而不是类型。 Const对象在概念上可能与非const对象不同,特别是在逻辑const的意义上(与bitwise-const相反)。因此,即使函数参数的常量正确性(或许)在POD的情况下过于谨慎,在对象的情况下也不是这样。如果函数与const对象一起工作,它应该这样说。请考虑以下代码段

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

ps:你可能会认为(const)引用在这里更合适并且给你相同的行为。好吧,对。只是给出了我在其他地方看到的不同的画面......

作为一名需要使用具有50多个公开函数的C ++程序的VB.NET程序员,以及偶尔使用const限定符的.h文件,很难知道何时使用ByRef或ByVal访问变量。 / p>

当然程序会告诉你在你犯错误的行上产生异常错误,但是你需要猜测2-10个参数中哪一个是错误的。

所以现在我有一个令人厌恶的任务,试图说服开发人员他们应该以一种允许自动创建所有VB.NET函数定义的自动方法的方式真正定义他们的变量(在.h文件中)。然后他们会沾沾自喜地说,<!>读取......文档。<!>

我编写了一个解析.h文件的awk脚本,并创建了所有的Declare Function命令,但没有关于哪些变量是R / O与R / W的指示,它只能完成一半的工作。

修改

在另一位用户的鼓励下,我正在添加以下内容;

这是一个(IMO)形成不良的.h条目的例子;

typedef int (EE_STDCALL *Do_SomethingPtr)( int smfID, const char* cursor_name, const char* sql );

我脚本生成的VB;

    Declare Function Do_Something Lib "SomeOther.DLL" (ByRef smfID As Integer, ByVal cursor_name As String, ByVal sql As String) As Integer

注意缺少<!> quot; const <!> quot;在第一个参数上。没有它,程序(或其他开发人员)不知道第一个参数应该传递<!>“ByVal。<!>”;通过添加<!>“const <!>”;它使.h文件自我记录,以便使用其他语言的开发人员可以轻松编写工作代码。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top