【问题标题】:Overloaded std::ostream operator<< not called, stream gets variable address instead of the object重载 std::ostream 运算符<< 未调用,流获取变量地址而不是对象
【发布时间】:2012-04-08 02:47:43
【问题描述】:

当我在指针上调用operator&lt;&lt; 时遇到问题。我搜索了 SO 并在 Google 上提出了我的问题,但所有提出的解决方案都没有解决我的问题。为了说明我的问题,请看我的一段简化代码:

Marker.h

class Marker {
     ...
public:
     friend std::ostream& operator<<(std::ostream& out, const Marker& marker);
     friend std::ostream& operator<<(std::ostream& out, Marker* marker);
};

inline std::ostream& operator<<(std::ostream& out, const Marker& marker) {
      out << "Marker " << marker._name << " of type " << marker._type << " at position " << marker._position;
      return out;
}

 inline std::ostream& operator<<(std::ostream& out, Marker* marker) {
      out << *marker;
      return out;
 }

Landmark.h

 class Landmark {
       ...
       Marker* m_marker;
       ...
 };

Landmark.cpp

 void Landmark::print( std::ostream& out )
 {
     out << "Marker GENERIC: " << m_marker << std::endl;
     //out << "Marker GENERIC: " << *m_marker << std::endl;
 }

这在 Visual Studio 2008 下没有链接。我收到大量 unresolved external symbol 错误。如果我删除friend std::ostream&amp; operator&lt;&lt;(std::ostream&amp; out, Marker* marker);,代码会编译,但不是预期的格式化输出,我只得到指向标记Marker* Landmark::m_marker 的指针的内存地址。取消注释第二行会使我的代码无法编译。

我应该如何重载operator&lt;&lt; 以获得正确的输出?

如果有任何帮助,我将不胜感激!

【问题讨论】:

  • 如果您尝试将std::ostream&amp; operator&lt;&lt;(std::ostream&amp; out, Marker* marker) 定义为它自己的编译单元(即.cpp 文件)中的(非内联)函数,那么这会让链接器满意吗?
  • 此时编译器不会争论,但包含此 DLL 的项目将无法编译:unresolved external symbol on function std::ostream&amp; operator&lt;&lt;(std::ostream&amp; out, Marker* marker)。这个函数应该在全局命名空间中定义吗?就我而言,我所有的类和重载的运算符函数都位于我的命名空间内。
  • @PavloDyban:它们在命名空间内,这不是问题。
  • 注意:对于指针重载,缺少两件事。它应该是Marker const* marker,因为Marker 实例不会以任何方式发生变异;它应该测试无效性,以防万一,在这种情况下可能输出&lt;null&gt;

标签: c++ operator-overloading iostream


【解决方案1】:

这是一个简单的例子:

#include <iostream>

namespace mine {
class Marker {
public:
     friend std::ostream& operator<<(std::ostream& out, const Marker& marker);
     friend std::ostream& operator<<(std::ostream& out, Marker* marker);
};

inline std::ostream& operator<<(std::ostream& out, const Marker& marker) {
     out << "Marker";
     return out;
}

inline std::ostream& operator<<(std::ostream& out, Marker* marker) {
     out << *marker;
     return out;
}
} // namespace mine

int main() {
  mine::Marker marker;
  mine::Marker* m = &marker;

  std::cout << m << "\n";
}

它可以工作as expected

您指向的错误是链接器错误,它告诉您编译器发出了对未发出函数的方法的调用。

我会假设你对我们撒了谎,或者 Visual Studio 又错了。

  • 如果你撒谎(即没有复制确切的代码):请注意,在定义inline 方法时,在使用它之前应该包含整个方法体,因此Landmark.cpp 应该包括方法定义。
  • 或者,可能有必要通过在与它们成为朋友之前预先声明函数来帮助 VS,以便 VS 了解它们确实存在于 mine 命名空间中,而不是全局命名空间中。

类似:

namespace mine {
  class Marker;

  std::ostream& operator<<(std::ostream& out, const Marker& marker);
  std::ostream& operator<<(std::ostream& out, Marker* marker);

  class Marker {
  public:
    friend std::ostream& operator<<(std::ostream& out, const Marker& marker);
    friend std::ostream& operator<<(std::ostream& out, Marker* marker);
  };

  inline std::ostream& operator<<(std::ostream& out, const Marker& marker) {
    out << "Marker";
    return out;
  }

  inline std::ostream& operator<<(std::ostream& out, Marker* marker) {
    out << *marker;
    return out;
  }
} // namespace mine

【讨论】:

  • 感谢您的广泛回复!向编译器解释函数位于本地命名空间中的第二个技巧已经奏效。问题可能在于我在operator
  • @PavloDyban:我在 VS2003 中遇到过这个错误(如果记忆正确的话),它与“递归”无关。通常friend 声明应该在类的命名空间中引入如此声明的函数,但似乎如果不存在先前的声明,VS 会偶然发现它。我不确定这是否是标准规定的(我希望这是一个错误)。
【解决方案2】:

现在,您有重载来获取对象的指针和引用。您正在传递一个指针,因此不会使用获取引用的重载。

您想扭转这种情况:摆脱采用指针的重载,并使用采用引用的重载。通过取消引用指针来使用它:out &lt;&lt; *m_marker;

【讨论】:

  • 谢谢!你给了我正确的建议!现在,如果我仍然想离开out &lt;&lt; "Marker GENERIC: " &lt;&lt; m_marker &lt;&lt; std::endl; 这一行,即通过传递我的标记对象的地址,我应该如何重载operator&lt;&lt;
【解决方案3】:

改变这个

friend std::ostream& operator<<(std::ostream& out, Marker* marker);

到这里

friend std::ostream& operator<<(std::ostream& out, Marker marker);

然后用这个

out << "Marker GENERIC: " << *m_marker << std::endl;

当您传递实际对象时,您的输出运算符期待一个指针。

【讨论】:

  • 根据Marker 的实现方式,按值传递它可能不是一个好主意——将第二个参数传递给流式操作符const Marker&amp; marker 可能更好,而不仅仅是@987654326 @.
  • 感谢您的评论!我用指针作为参数隐藏了重载,而是使用了引用指针。这与您通过按值传递标记所建议的几乎相同。但是,我希望out &lt;&lt; "Marker GENERIC: " &lt;&lt; *m_marker &lt;&lt; std::endl; 行中没有星号。我应该如何重载我的运算符以获取指向Marker 的指针并输出对象的内容?
猜你喜欢
  • 2018-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-03
相关资源
最近更新 更多