【问题标题】:C++ What is wrong with using a toString() methodC++ 使用 toString() 方法有什么问题
【发布时间】:2015-01-31 21:07:45
【问题描述】:

我刚刚遇到了this question,这是关于如何能够通过打印对象

std::cout << x << std::endl;

据我了解,实现此目的的标准方法是重载 ostreams

替代方法(也作为上述问题的答案)是覆盖字符串转换运算符。然而,这伴随着导致“无意转换和难以追踪的错误”的警告。

现在我想知道编写一个 toString() 方法然后通过它使用它是否有任何缺点

std::cout << x.toString() << std::endl;

【问题讨论】:

  • 你将如何为int 做到这一点?还是你的意思是to_string
  • 因为使某些东西可流式传输与将其转换为字符串不同。如果您希望您的类型可流式传输,请重载 ostream&amp; operator&lt;&lt;。如果你想用它制作字符串,给它一个 to_string 成员。

标签: c++ string operator-overloading iostream cout


【解决方案1】:

输出流处理输出格式和输出。因此,使用您的 toString() 方法,客户端将无法像处理其他所有内容一样管理对象的格式:

// set specific formatting options for printing a value
std::cout << std::scientific << std::setprecision(10) << 10.0 << '\n'; // prints 1.0000000000e+01

// set formatting based on user's cultural conventions
std::cout.imbue(std::locale(""));
std::cout << 10000000 << '\n'; // depending on your system configuration may print "10,000,000"

也许您不关心允许任何格式,所以也许这无关紧要。

另一个考虑因素是,输出到流不需要将整个字符串表示形式同时保存在内存中,但您的 toString() 方法可以。


其他人已经指出了这一点,但我认为更清晰的说法是,您的类接口不仅限于它提供的方法,还包括您围绕它构建的其他函数,包括非成员函数,例如作为您提供的operator&lt;&lt; 重载。即使它不是您的类的方法,您仍应将其视为您的类接口的一部分。

这是一篇讨论这个问题的文章,也许你会发现它对你有帮助:How Non-Member Functions Improve Encapsulation


这是一个为用户定义的类重载operator&lt;&lt; 的简单示例:

#include <iostream>

struct MyClass {
  int n;
};

std::ostream &operator<< (std::ostream &os, MyClass const &m) {
  for (int i = 0; i < m.n; ++i) {
    os << i << ' ';
  }
  return os;
}

int main() {
  MyClass c = {1000000};
  std::cout << c << '\n';
}

【讨论】:

  • 抽象类呢?我可以有一个 toString()=0;但是我如何声明任何子类都应该带有重载的
  • 也许我应该先阅读您发布的链接;)第一点说“如果(f 需要是虚拟的)使 f 成为成员函数”。
  • @tobi303 是的,现在虚拟函数必须是成员。未来非成员函数可能会获得对虚拟调度的支持:stroustrup.com/multimethods.pdf
【解决方案2】:

据我了解,实现此目的的标准方法是重载 ostreams

这是一件好事。你的班级越小越好。如果流行的 C++ 习语允许您在课堂上多做一件事,为什么不遵循它呢?

现在我想知道编写 toString() 方法是否有任何缺点

缺点是:

  • operator&lt;&lt; 以统一的方式与内置类型(如 int)和用户定义类型一起工作。 toString 只能用于课程
  • C++ 比 Java 更加异构。 std::string 是最流行的字符串,但仍然存在其他字符串类并使用它们。
  • 必须创建字符串,这可能会影响性能。如果你直接写到流媒体,你会避免它。

【讨论】:

  • 与使用任何类封装的统一序列化方法的优点相比,这些缺点的权重要小得多。为什么我们要关心诸如不使用 std::string 之类的不良做法?
【解决方案3】:

它们是根本不同的东西。提供operator&lt;&lt; 重载有效地扩展了流的接口,使您的类类型的对象可流化。提供toString 函数可以扩展您的类的接口,从而可以从您的类中获取std::string。这些代表不同的事物。

您的类的接口应该完全对应于它在您的程序逻辑中所代表的内容(单一职责)。 toString 很少是类接口的自然部分。但是,扩展流的接口以接受更多对象更符合逻辑。

也就是说,在一种情况下,您会说“现在您可以流式传输此类类型的对象”。在另一种情况下,您说“您可以将此类类型的对象转换为std::string。” - 碰巧std::string 然后是可流式传输的。想一想——我的Person 类有一个toString 函数真的有意义吗?我什么时候可以把人变成文字了?

【讨论】:

  • 我想这是否有意义是一个品味问题。在我看来,任何 Java 对象都可以通过 toString() 转换为字符串是完全有道理的
【解决方案4】:

你的第一个假设是错误的。您无需在 ostream 中进行任何更改。

像 operator

【讨论】:

    【解决方案5】:

    每个类都有一个 toString() 函数没有任何问题。它的优点是更明确,可以多态化,并且可以在流媒体以外的其他情况下使用,但是在与团队合作时需要有一个编码规则(“是 to_string() 还是 str() ,还是流式传输()?”)。

    重载运算符std::cout << x; 时会期望重载运算符

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-08
      • 2013-03-14
      • 1970-01-01
      • 2016-04-13
      • 2011-04-24
      • 2018-07-28
      • 1970-01-01
      相关资源
      最近更新 更多