【问题标题】:Overloading the C++ assignment operator重载 C++ 赋值运算符
【发布时间】:2013-03-30 10:08:27
【问题描述】:

我想用一些功能扩展std::string,所以我从中派生出我的String。为了使String str = stdStr; 之类的代码工作,我尝试重载赋值运算符,但由于某种原因我的代码没有被调用。我该如何解决?

#include <string>

class String
    :
        public std::string
{

    public:
        /*
        I do know that this constructor will solve the problem, but is it possible to use the operator?

        String ( const std::string& stdString )
        {

            ...

        }
        */

        String& operator= ( const std::string& stdString )
        {
            ...
            return *this;
        }

};

int main()
{

    std::string foo = "foo";
    String bar = foo;

    return 1;

}

【问题讨论】:

  • 你确定继承是正确的扩展方式吗?
  • 我认为扩展字符串是个坏主意。不可能说出你的新功能是什么,但我可以非常自信地说继承不是实现它的方法。
  • 最糟糕的是你叫它String
  • 扩展标准 STL 类通常被认为是一个非常的坏主意。仅在大小写不同的命名是另一个被认为是这样的事物。
  • @UmNyobe 它在我的个人命名空间中。有什么问题?

标签: c++ operator-overloading variable-assignment assignment-operator overloading


【解决方案1】:
String bar = foo;

是拷贝初始化(相当于

String bar(String(foo));

),而不是赋值。您应该为此工作实现复制构造函数(或默认初始化变量,然后将foo 分配给bar)。

无论如何,从标准 C++ 类型派生是个坏主意,因为这些类型没有虚拟析构函数。而且组合比继承更好,在你的例子中,你应该使用组合。

【讨论】:

  • 感谢您的回答。让std::string 成为我的String 的成员并扩展它的功能是不是更好?
  • 是的,最好让std::string成为String的成员。
  • @Kolyunya:没有虚拟 dtor 的事实是一个非常弱的论点。字符串是 VALUES。它们永远不会被分配到newstd::string* 并因此被删除。正确使用string-s 和String-s 不会受到任何UB 的影响。 String-s(以及string,不是 OOP 对象,因此此处不能应用 OOP 规则:必须应用 VALUE 算术规则)
  • @riv:你试过吗?你的老板是否在浪费你的时间重写所有 112 个 std::string 原型而付钱给你?
【解决方案2】:

String bar = foo 这行不是赋值,它实际上等同于String bar(foo)。如果你写

String bar;
bar = foo;

您的赋值运算符将按预期被调用。

【讨论】:

  • 它们并不完全等同。一个是直接的,另一个是复制-初始化。
  • 当然,有一些细微的差别,如stackoverflow.com/questions/1051379 - 但在这种情况下它们不适用。
【解决方案3】:

这里的问题是你的线

String bar = foo;

不调用赋值运算符,因为你没有要赋值的对象(bar 尚未创建);它调用一个构造函数。事实上,如果可用的话,它会调用你注释掉的构造函数。

如果你真的想使用你的操作符,你必须写:

String bar;
bar = foo; // Now bar exists and you can assign to it

顺便说一句,从std::string 继承并不是一个好主意,因为与标准库中的大多数其他类一样,该类并非旨在继承自。具体来说,它缺少一个虚析构函数,如果你以多态方式使用它会带来麻烦,例如:

std::string* str = new String();
delete str; // Oops; the wrong destructor will be called

【讨论】:

  • 技术上已正确,但是...“不派生”学校附属机构中的某个人能否告诉我应该使用多态字符串的原因?我编程 30 年了,在过去的生活中,我从未见过任何字符串派生类用户的 std::string* str = new String();
  • @EmilioGaravaglia:这是“与可能的错误的距离”的问题。从没有虚拟析构函数的类派生本身并不是错误,但是拥有这样的类会使您的客户端 更接近 出错,因为现在您必须 document 行为的特殊性。我们都知道发生的可能性有多大。 (写作和阅读。)对于初学者,你倾向于给出一般性的建议,这次是“不要扩展标准类”。
  • @EmilioGaravaglia 添加到 DevSolar 所说的内容中,这条线可能不太可能,但将 String* 传递给采用 std::string* 的函数则不然。传递引用也会带您进入多态行为,尽管我承认您不会尝试删除通过引用收到的对象。当然,如果你知道危险,你可以采取措施来对付它(例如:在你的代码中强制使用String而不是std::string),但危险仍然存在。
  • @EmilioGaravaglia:没有虚拟析构函数的类和从这样的类派生的类之间存在细微差别,你不觉得吗?并且不要给我这种“如果你知道你的东西就很明显”的谈话。经过十多年的交易,我学会了不要过多地假设下一个人的能力水平(或者他将如何使用特定的课程)。有好几次我自己都被扔进了一个我不太了解这门语言的环境中,并且真的很喜欢一位体贴的编码员留下的深思熟虑的评论。
  • @EmilioGaravaglia:对你的诱饵不感兴趣,抱歉。
【解决方案4】:

在您的情况下,despte = present,将创建新的 String 对象,因此需要

String ( const std::string& stdString )  

取消注释。 另一种选择是做类似

String bar;  
bar = foo;  

但这听起来不是个好主意。

【讨论】:

    【解决方案5】:

    这样做会使用复制构造函数:

    String foo("foo");
    String foo="hello";//assignment operator here
    

    但这不是:

    String foo;
    String foo="hello";//copy constructor used here since foo was not initialized
    

    【讨论】:

      【解决方案6】:

      其他赞成的答案是消息灵通的。

      为了直接回答您的问题,您正在尝试这样做。

          String& operator= ( const std::string& stdString )
          {
              // Call the base implementation
              return std::string::operator= ( stdString );
          }
      

      【讨论】:

        【解决方案7】:

        std::string foo = "foo"; 字符串 bar = foo;

        您在main中尝试的操作不会调用复制赋值运算符。它相当于调用复制构造函数。

        最好避免继承 std::string 因为它没有定义虚拟析构函数。

        【讨论】:

          猜你喜欢
          • 2016-08-30
          • 1970-01-01
          • 1970-01-01
          • 2018-12-27
          • 2012-04-22
          • 2015-06-01
          • 2012-08-17
          • 2011-01-27
          • 2011-05-31
          相关资源
          最近更新 更多