【问题标题】:Why is a string passed as const string in a function parameter为什么在函数参数中将字符串作为 const 字符串传递
【发布时间】:2015-04-27 22:22:23
【问题描述】:

我对教科书中的一个例子有点困惑。创建字符串时,它被创建为类型string。但是,当将相同的字符串传递给函数时,函数参数是const string,而不是string

以下是部分代码:

int main()
{
    string str;
    cout << "blah blah..";
    getline(cin, str);
    if (is_pal(str))
    //...
}

bool is_pal(const string& s)
{
    //...
}

为什么函数参数是const string&amp; s 而不仅仅是string&amp; s?我已经阅读了我的教科书,但似乎找不到任何解释。

【问题讨论】:

  • 也许字符串可以改变,但函数应该不能?
  • 字符串被传递到几个函数中,其中一些函数确实会改变字符串,例如删除标点符号,将其更改为小写等。
  • 但是它们是改变了字符串的原始值(即通过内存地址引用它)还是只获取传递的参数并返回它的副本?
  • 这只是表明名为is_pal 的特定函数不会修改其参数。阅读我在 C++ 中对const 的解释:*.com/a/3709257/103167
  • 我正在搜索副本,但失败了。但是,我发现了一个问题和很好的答案,您可能会发现它们与主题很接近并且很有趣:*.com/questions/3967177/…

标签: c++ string


【解决方案1】:

复制成本可能很高的对象,例如std::string,在 C++ 中经常通过 const 左值引用传递。这是一个很常见的成语;你会到处看到它。一个 const 左值引用可以绑定到左值和右值而不需要复制,因此这是一种将字符串传递给不会修改它们的函数的有效方法。

【讨论】:

    【解决方案2】:

    当函数在其参数上使用const 时,通常意味着该函数不会更改参数。

    在编写自己的函数时,应确定函数是否打算修改参数,并相应地使用或不使用const

    同样,当你使用别人写的函数时,注意函数是否打算修改你的对象。如果函数接受非const 引用,您就会知道这一点。

    void foo_will_not_modify (const std::string &x); // const ref - won't modify
    void bar_will_not_modify (std::string x);        // copy - won't modify
    void baz_will_modify (std::string &x);           // reference - can modify
    

    【讨论】:

    【解决方案3】:

    回复

    为什么函数参数是const string&amp; s而不是string&amp; s

    一个主要原因是后者不能绑定到字符串文字或普通函数结果或字符串运算符的结果,例如+,称为“右值”。

    至少在标准 C++ 中是这样,但 Visual C++ 允许将其作为一种不幸的语言扩展。

    另一个原因通常是函数的作者认为当它承诺不修改其参数时会更有用。或者至少,这会让使用它的代码更容易推理。


    例子:

    // To see the problem also with Visual C++, use that compiler's /Za option.
    #include <iostream>
    #include <string>
    using namespace std;
    
    void goodwrite( const string& s ) { cout << s << '\n'; }
    
    void badwrite( string& s ) { cout << s << '\n'; }
    
    auto main() -> int
    {
        // Good:
        goodwrite( "The answer is " + to_string( 6*7 ) + "." );
    
        //! Uh oh, doesn't compile with standard C++:
        badwrite( "The answer is " + to_string( 6*7 ) + "." );
    }
    

    【讨论】:

    • const 引用或值复制参数将接受计算的临时参数 (+1)。
    • “后者不能绑定到字符串文字或普通函数结果或字符串运算符(如+)的结果”是什么意思。抱歉,我对 C++ 有点陌生,所以我对词汇不是很熟悉。编译器的 /Za 选项是什么意思?
    • @ruisen:实际上,绑定(使引用引用某物)意味着您不能使用字符串文字的实际参数。并且您不能使用作为普通函数调用或运算符表达式的实际参数。编译器选项用于修改编译器的行为,例如让它遵循标准(这就是这个标准)。它们在编译器的调用中指定。在 IDE 中,您通常会勾选或不勾选各种选项,然后 IDE 在调用编译器时生成相应的编译器选项。
    【解决方案4】:

    传递const 引用意味着函数会将参数视为常量,并且不会以任何方式修改它。这允许使用 const 值调用此函数 - 显式定义为 consts 或隐式常量,例如字符串文字。

    【讨论】:

    • 但是——重要的是——不仅仅是 const 值。将可写字符串作为实际参数传递给正式的 const 参数是非常合法的。
    • 更不用说你不能将右值作为非常量引用参数传递,所以像is_pal("dddd") 这样的操作将不起作用,除非is_pal 将字符串通过const 引用价值。
    【解决方案5】:

    您可以不使用“const”关键字来执行此操作。

    如果您的函数不修改参数,最好使用“const”关键字作为参数。所以,一不小心修改参数,就会报错。

    这只是最好的编码实践,如果函数没有修改参数,我建议你也这样做。

    【讨论】:

      【解决方案6】:

      TL;DR:确保对象在功能上不被修改并保存一次复制操作。


      对于const 对象,只能调用const 方法,并且该方法不能改变对象的状态:

      class A {
          int i = 10;
          mutable int j = 10;         // Explicitly allowed to change even for const objects
      
      public:
          void f() { ++i; }           // OK: non-const function changes object
          void g() const { ++i; }     // Error: const function changes object
          int h() const { return i; } // OK: const function doesn't change object
          int s() const { return ++j; } // OK: const function changes mutable field 
      };
      
      void foo(const A& a) {
          a.f();      // Error: only const methods are allowed
          a.g(); a.h(); a.j(); // OK
      }
      

      如您所见,没有从函数foo 修改字段i 的干净方法,但您可以使用h()s() 方法读取A 字段。

      您还可以通过将副本传递给它来确保您的本地对象不会被被调用函数修改:

      void foo(A a);
      bool is_pal(std::string s);
      

      但复制可能会很昂贵,所以你必须通过引用传递:

      void foo(A& a);
      bool is_pal(std::string& s);
      

      为了确保对象在调用你的函数之前具有相同的状态,你必须向它添加const 限定符。 Scott Meyers 的书,Effective C++,第三版“第 20 条:Prefer pass-by-reference-to-const to pass-by-value”中解释了这个习语。

      【讨论】: