【问题标题】:Can operator<< in derived class call another operator<< in base class in c++?派生类中的运算符<<可以在c ++中调用基类中的另一个运算符<<吗?
【发布时间】:2013-10-31 15:32:21
【问题描述】:

在我的代码中,Manager 派生自 Employee,它们每个都有一个 operator&lt;&lt; 覆盖。

class Employee{
protected:
    int salary;
    int rank;
public:
    int getSalary()const{return salary;}
    int getRank()const{return rank;}
    Employee(int s, int r):salary(s), rank(r){};
};
ostream& operator<< (ostream& out, Employee& e){
    out << "Salary: " << e.getSalary() << " Rank: " << e.getRank() << endl;
    return out;
}

class Manager: public Employee{
public:
    Manager(int s, int r): Employee(s, r){};
};
ostream& operator<< (ostream& out, Manager& m){   
    out << "Manager: ";
    cout << (Employee)m << endl;  //can not compile, how to call function of Employee?
    return out;
}

我希望cout &lt;&lt; (Employee)m &lt;&lt; endl; 会调用ostream&amp; operator&lt;&lt; (ostream&amp; out, Employee&amp; e),但它失败了。

【问题讨论】:

  • 编译错误是什么?
  • 您正在切片(至少代码正在尝试;查找)。你需要写static_cast&lt;Employee&amp;&gt;(m) 而不是(Employee)m
  • @ScarletAmaranth 它应该无法编译,因为临时的Employee 无法绑定到Employee&amp;。如果您使用的是 MSVC,那么它有一个非常愚蠢的扩展,允许这种危险行为。
  • (Employee)m 将生成 m 的 Employee 部分的副本,这是一个临时的。我不认为它可以匹配你的重载的Employee &amp; e 参数。
  • @Simple:幸运的是 g++ 会因此而严厉惩罚你。 :)

标签: c++ operator-overloading operator-keyword derived-class


【解决方案1】:

转换为引用而不是副本:

cout << (Employee&)m << endl;  //can not compile, how to call function of Employee?

另请注意,ostream 运算符绝不是该类的成员(从问题的标题来看,您似乎对此感到困惑)。

【讨论】:

    【解决方案2】:

    cout &lt;&lt; (Employee)m &lt;&lt; endl; 更改为cout &lt;&lt; (Employee&amp;)m &lt;&lt; endl;

    错误信息的解释是这样的:

    当您尝试演员 (Employee)m 时,您正在创建一个临时的。 operator&lt;&lt; 重载需要一个引用。您不能引用临时文件。

    由于您真的只是希望它打印自己的数据(而不是复制自身),因此您将拥有的引用转换为所需类型的引用(它是基类)。

    正如 jrok 在他的 answer 中指出的那样,您还可以通过提供一个虚拟函数来打印数据来实现这一点。这将是一种更简单的方法,因为它不需要您为每个派生类重载 operator&lt;&lt;

    【讨论】:

    • @DavidRodríguez-dribeas 我就是这么说的。我错过了什么吗?
    • 对不起... 错过了一些东西
    【解决方案3】:

    执行此操作的通常方法是在派生类可以覆盖的基类中具有(可能是privateprotected)虚拟print(或任何其他合适的名称)函数。

    您只提供通用的operator&lt;&lt; 以引用基类并在其中调用print。如果需要,覆盖的print 函数可以调用基类的print

    【讨论】:

    • 他的operator&lt;&lt; 实际上完全在类之外。
    • @ZacHowland 我认为这没有问题。您可以像往常一样声明它们 friend 用于流运算符。
    • @jrok。对不起。我只是不认为这是对实际 OP 问题的答案。即使你说的是对的。
    • 我没有投反对票,但是当问题可以用静态类型解决时,引入多态性似乎很可惜。
    • @jrok 我并没有把它说成一个问题,只是作为一个注释(你第二段的第一句话暗示它是一个班级成员——它可以是,但不在这种特殊情况)。
    【解决方案4】:

    你的超载:

    ostream& operator<< (ostream& out, Employee& e)
    

    仅适用于对 Employee 对象的引用,因此它不适用于非引用值(如强制转换的结果)。

    通常,原型是:

    ostream& operator<< (ostream& out, const Employee& e)
    

    这也保证了打印 Employee 不会改变它。如果你改变它,事情应该会正常工作。 (ostream&amp; 确实必须是一个引用,而不是一个 const 引用,因为 ostream 被打印操作改变了。)

    【讨论】:

      【解决方案5】:

      有两个问题共同导致你的编译失败。

      (1)本声明及定义:

          ostream& operator<< (ostream& out, Employee& e){
              out << "Salary: " << e.getSalary() << " Rank: " << e.getRank() << endl;
              return out;
          }
      

      尽管您没有更改e,但您将其引用到非const。这禁止您在右值上调用此运算符。

      (2) 这一行

      cout << (Employee)m << endl;
      

      正如其他人所说,您正在切片m。此外,演员(Employee) m 返回一个临时的Employee,它是一个右值。

      总而言之,转换 (Employee)m 会产生一个无法绑定到 e 的右值。

      您可以修复其中一个或另一个以使您的代码编译器,但最好同时修复这两个问题以使您的代码更健壮:

      // ...
      ostream& operator<< (ostream& out, const Employee& e) {
      // ...
      
      // ...
      cout << static_cast<Employee&>(m) << endl;
      // ...
      

      【讨论】:

        【解决方案6】:

        首先,您重载了一个全局函数。这是除覆盖之外的另一个概念。见Override and overload in C++

        接下来,你的演员阵容是错误的。应该这样做:

        ostream& operator<< (ostream& out, Manager& m){   
            out << "Manager: ";
            cout << static_cast<Employee&>(m) << endl;  //can not compile, [...]?
           return out;
        }
        

        ...如果您真的想重载全局 &lt;&lt; 运算符。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-07-03
          • 2012-01-15
          • 2017-03-13
          • 2021-07-02
          • 1970-01-01
          • 2020-11-24
          • 2012-06-21
          • 2021-07-31
          相关资源
          最近更新 更多