重载运算符和修改字符串

Overloading operator and modifiyng string

我正在学习运算符重叠。我创建了简单的 class 来测试它。

class Beer{
public:
   Beer(int oner , int twor , string name){
   this -> one = oner;
   this -> two = twor;
   this -> name = name;
   };
    int getOne(){

        return this -> one;
    };
    int getTwo(){

        return this -> two;
    };
    string getName(){
        return this -> name;
    };
    Beer operator + (const Beer &a)const {

        return Beer(5,two+a.two,"firstName");

    };
    Beer operator + (string a)const {

       this -> name =  this -> name +" "+a;

    };

private:

   int one;
   int two;
   string name;
};

我想弄清楚,如何使用重载的操作数对字符串进行 midify。我声明的函数

Beer operator + (string a)const {
      this -> name =  this -> name +" "+a;
};

抛出有关传递常量字符串的错误。

我尝试使用

Beer operator + ( const string *a)const { 
       swap(this -> name , this -> name + " " + a);
       return *this;
    };

抱怨一个是 cosnst 字符串,第二个是基本字符串。

这个想法很简单。

Beer one ( 5, 6, "one")
one + "two"

// one.name = "one two"

怎样才是正确的做法?

// 交换错误

error: no matching function for call to 'swap(const string&, std::basic_string<char>)'|

// 字符串错误

passing 'const string {aka const std::basic_string<char>}' as 'this' argument of 'std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(std::basic_string<_CharT, _Traits, _Alloc>&&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]' discards qualifiers [-fpermissive]|

在您的 operator+() 此处:

Beer operator+( string a ) const 
{
    this->name = this->name + " " + a;
};

函数签名上的const是对编译器的保证,当函数被调用时,你不会改变对象中的数据,但你改变了对象中的数据。

Beer operator + (string a)const {
  this -> name =  this -> name +" "+a;
};

你不应该改变被调用的 + 运算符的对象的内容,毕竟如果你执行 A = B + C,B 的内容不应该改变。编译器正确地通知了您这一点,因为它是一个 const 函数。

而是创建一个临时对象来保存 'sum' 和 return 它。

Beer operator + (string a)const {
    return Beer(one, two, name + " " + a);  
};

评论:

  1. 不要包含整个 std 命名空间。您很可能 运行 与您自己的代码发生严重的名称冲突。最多,使用您明确需要的符号,例如using std::string;.

  2. 除非你需要一个值的副本来修改,否则通过 const 引用传递像 std::string 这样的大对象。当您将参数声明为具有值类型 std::string 时,您会收到该字符串的副本,除非您需要副本以在函数内部进行修改,否则这是昂贵的。

    这是 C++ 标准长期存在的问题:像这样的实现细节,本应与函数的用户无关,却泄漏到接口(函数的声明)中。尽管如此,当有一份副本有意义时,让编译器给你一份,而不必输入那么多。因此:

    // prefer this
    std::string fooize(std::string foo) {
      assert(foo.size() > 0);
      foo.insert(1, "foo");
      return foo;
    }
    // over this
    std::string fooize(const std::string & bar) {
      assert(bar.size() > 0);
      auto foo = bar;
      foo.insert(1, "foo");
      return foo;
    }
    
  3. 使用初始化列表,你就不需要做愚蠢的名字体操(你有 onertwor 名字:

    Beer(int one, int two, const std::string & name) :
      one(one),
      two(two),
      name(name)
    {}
    
  4. 声明只读访问器 const:

    int getOne() const { return one; }
    
  5. Return const 引用的大值,如字符串;用户代码可能会让编译器在需要时自动帮助制作副本:

    const std::string & getName() const { return name; }
    
    // use:
    Beer beer{0,0,""};
    std::cout << (beer.getName() + "!") << std::endl; // makes a copy of name as needed
    
  6. 在接受字符串的 + 运算符中,您应该 return 一个新对象,而不是修改 this。你几乎应该按照其他操作员的方式去做 + 你已经做到了。

    Beer operator +(const std::string & a) const {
      return Beer(one, two, name + " " + a);
    };
    
  7. 如果你想修改你的对象,你想要operator +=:

    Beer & operator+=(const std::string & a) {
      name += " ";
      name += a;
      return *this;
    }
    
  8. 即使您的 class 旨在试验运算符,您也应该始终考虑运算符是否让生活更轻松。例如,您 class 有三个成员。除非从 class 的语义中清楚地看出,否则这些成员中的哪些将被操作并不是很明显。例如,将方法命名为 addToOneaddToTwoappendToName 会更清晰,而不是运算符,或者只是让用户通过设置器设置成员,喜欢 setOne(int one) { this->one = one; }。然后用户只需执行 beer.setOne(beer.getOne() + 2);.

  9. 考虑在没有 get 前缀的情况下命名 getter,例如

    class Beer {
      int m_one;
    public int one() const { reeturn m_one; }
    };
    

    用户输入更少。标准库以及 boostQt 等大型库都遵循此约定,例如你有std::string::size(),没有std::string::getSize(),等等