【问题标题】:Where to put overload<< code?在哪里放置重载<<代码?
【发布时间】:2012-11-22 16:24:08
【问题描述】:

我正在尝试重载流运算符 toString() 函数返回字符串的类 Foo,使用以下代码:

std::ostream &operator<<( std::ostream &flux, Foo const& foo )
{
    flux <<  foo.toString(); 
    return flux;
}

为了在main.cppfile 中使用它

我的问题是:把那段代码放在哪里?

  • 如果我将它放在main.cpp 中,在它使用之前它运行良好,但我可能想在其他文件中使用它。
  • 如果我将它放在 foo.cpp 中,我会收到“没有这样的功能”错误:

    src/main.cpp:77: error: no match for ‘operator<<’ in ‘std::cout << foo’
    

    这是有道理的,因为代码不包含在 main.cpp 文件中

  • 如果我将它放在 foo.hclass 标头中,在类声明之外,我会收到“多重定义”错误:

    foo.o: In function `operator<<(std::basic_ostream<char, std::char_traits<char> >&, Foo const&)':
    foo.cpp:(.text+0x0): multiple definition of `operator<<(std::basic_ostream<char, std::char_traits<char> >&, Matrix const&)'
    bar.o:bar.cpp:(.text+0x0): first defined here
    

    foo.h 标头确实包含在不同的类/文件中,但是有一个 ifdef 保护,所以我不明白这一点。

那我该怎么办?

【问题讨论】:

    标签: c++ operator-overloading


    【解决方案1】:

    有多种选择:

    在标题中声明,在Foo之后,在Foo.cpp中定义。

    //foo.h
    class Foo
    {};
    std::ostream &operator<<( std::ostream &s, Foo const& foo );
    
    //foo.cpp
    #include "foo.h"
    std::ostream &operator<<( std::ostream &s, Foo const& foo )
    {
        return s;
    }
    

    在类定义中将其定义为friend

    //Foo.h
    class Foo
    {
       friend std::ostream &operator<<( std::ostream &s, Foo const& foo )
       {
          return s;
       }
    };
    

    在头部定义,在类定义之外,标记为inline,防止多重定义。

    //Foo.h
    class Foo
    {
    };
    
    inline std::ostream &operator<<( std::ostream &s, Foo const& foo )
    {
       return s;
    }
    

    【讨论】:

      【解决方案2】:

      将声明放在头文件中,将定义放在cpp文件中。

      也就是放

      std::ostream &operator<<( std::ostream &flux, Foo const& foo );
      

      在你的头文件中,然后放

      std::ostream &operator<<( std::ostream &flux, Foo const& foo )
      {
          flux <<  foo.toString(); 
          return flux;
      }
      

      在您的 cpp 文件中。

      这样,你可以在任何你想要的地方包含头文件,并且可以使用该函数,但它只会被定义一次,所以你不会得到multiple definitions错误。

      【讨论】:

      • 谢谢,它有效。我不知道为什么我没有想到这一点。
      【解决方案3】:

      将重载的原型放在类声明中,将其实现放在Foo 实现中(或者如果你想要inline,把它放在Foo 声明中)。

      所以要么:

      // foo.h
      #ifndef FOO_H_ASDSADKJSLADJL
      #define FOO_H_ASDSADKJSLADJL
      class Foo {
      };
      std::ostream& operator<< (std::ostream &, Foo const &);
      #endif // FOO_H_ASDSADKJSLADJL
      

      // foo.cpp
      #include "foo.h"
      ....
      std::ostream& operator<< (std::ostream &os, Foo const &) {
          ....
          return os;
      }
      

      #ifndef FOO_H_ASDSADKJSLADJL
      #define FOO_H_ASDSADKJSLADJL
      class Foo {
      };
      inline std::ostream& operator<< (std::ostream &os, Foo const &) {
          ....
          return os;
      }
      #endif // FOO_H_ASDSADKJSLADJL
      

      编译器没有找到它的错误是缺少重载原型。

      【讨论】:

      • 我认为投反对票是因为您的第一个答案中包含 'nineteenmorecharacters'。
      • 不,仍然不正确。您在 foo.cpp 中有一个无与伦比的“#endif”。此外,“把它放在 Foo 附近”的表述是废话。
      • @Xymotech:我承认这很不寻常,但我认为这不会使包含的答案无效。
      • @phresnel 我不是那个投反对票的人,当我看到它发生时,我只是在写我的答案。您的编辑大约在 15 秒后完成,但损坏已经造成。
      • +1 因为它是正确的,但你必须承认原来的“把它放在 Foo 附近”几乎没用。 FGITW 仅在初始答案正确且完整的情况下才有效。 :P 相信我,我知道。
      【解决方案4】:

      在你声明Foo的标题中声明它,在你定义Foo方法的源中定义它。

      【讨论】:

        【解决方案5】:

        与 C 和 C++ 一样,您应该

        1. 将实际代码放入相应的.c / .cpp 文件中——这里我推荐foo.cpp
        2. 预声明放入适当的.h/.hpp 头文件,并为从主c/cpp 文件外部使用的所有内容添加适当的#ifdef 保护。

        在某些情况下,您可以使用#define 宏或inline 函数。这应该在标题中完成;由于内联,不应该有多重声明错误。

        技术上你可以做很多事情。我建议坚持在头文件中定义 API 并将实现放入代码文件的最佳实践。这是最可扩展的。 inline hack 将适用于这种情况,但如果您稍后决定增强该方法并删除内联,它会令人惊讶地崩溃。

        标头/代码拆分最不可能中断。

        【讨论】:

          猜你喜欢
          • 2015-06-29
          • 2015-10-31
          • 2010-11-16
          • 2015-09-19
          • 1970-01-01
          • 1970-01-01
          • 2011-08-02
          • 2012-08-20
          • 1970-01-01
          相关资源
          最近更新 更多