【问题标题】:using ostream as function argument使用 ostream 作为函数参数
【发布时间】:2017-08-04 14:31:45
【问题描述】:

我在理解 ostream 到底是什么方面存在一个基本问题。我知道它是输出流的基类,但我不知道什么时候使用它以及为什么要使用它,而不仅仅是说 std::cout。 所以在这里我有这个例子,我必须使用 pop() 函数创建一个名为 stack 的新类(就像在 C++ 已经提供的类中一样)。

这里的 list_node 是一个结构,它由两个元素组成:键(它是一个整数)和一个指向下一个整数的插入器。

list_node的定义(已经给出):

struct list_node {
int key;
list_node∗ next;
// constructor
list_node (int k, list_node∗ n)
: key (k), next (n) {}
};

这是类的定义(也已经给出):

class stack {
public:
void push (int value) {...}
...
private:
list_node∗ top_node;
};

这是我遇到问题的部分:

void print (std::ostream& o) const
{
const list_node* p = top_node;
while (p != 0) {
o << p->key << " "; // 1 5 6
p = p->next;
}
}

我不明白他们为什么使用 ostream& o 作为函数参数。难道他们不能只是将 top_node 作为参数并在其上使用 .next 函数(.next 读取下一个 list_node),然后他们就可以使用 std::cout 函数打印它。为什么像他们那样做会更好?

【问题讨论】:

  • 首先,std::ostream 肯定不是std::cout 的另一个名称。前者是基类,后者是未指定类型的对象。其次,不清楚你在问什么。
  • 这个方法是在栈里面定义的吗? top_node 是堆栈类的成员吗?真的不清楚你在问什么。看起来有很多缺失的上下文。
  • @SergeyA,我编辑了它。我希望现在更容易理解了。

标签: c++ class ostream


【解决方案1】:

为什么像他们那样做会更好?

我不确定你的问题,也不确定这是一个更好的方法。

也许目的是为了灵活。这是我的应用程序库中的一个示例:


当我将数据属性声明为 ostream 时

class T431_t
{
   // ...
   std::ostream*      m_so; 
   // ...

我可以简单地使用该属性将报告传递到“where-m_so-points”。在这个应用程序中,有几个使用 *mso

inline void reportProgress()
{
    // ...
   *m_so << "  m_blk = " << m_blk
         << "  m_N = 0x" << std::setfill('0') << std::hex << std::setw(16) << m_N
         << "  " << std::dec << std::setfill(' ') << std::setw(3) << durationSec
         << "."  << std::dec << std::setfill('0') << std::setw(3) << durationMSec
         << " sec  (" << std::dec << std::setfill(' ') << std::setw(14)
         << digiComma(m_N) << ")" << std::endl;
   // ...
}

请注意,在类构造函数(ctor)中,m_so 有一个默认赋值给 std::cout。

T431_t(uint64_t maxDuration = MaxSingleCoreMS) :
      // ..
      m_so   (&std::cout), // ctor init - default
      // ..
 {
    // ...

当用户选择双线程处理选项时,这是一个命令行选项,通过使用桌面的两个处理器在大约 1/2 的时间内执行应用程序,如果我允许报告可能会变得难以阅读两个独立的输出流交织在一起(在用户屏幕上)。因此,在线程 2 运行的对象实例中,m_so 被设置了一些不同的东西。

以下数据属性捕获并保存线程 2 输出以供以后流式传输到 std::cout。

std::stringstream  m_ssMagic; // dual threads use separate out streams

线程 2 启动,线程将其设置为私有 m_so:

void exec2b ()  //  thread 2 entry
{
    m_now = Clock_t::now();

    m_so = &m_ssMagic; // m_so points to m_ssMagic 

    // ...

    m_ssMagic << "   execDuration =  " << m_ssDuration.str() 
              << " (b)  " << std::endl;
} // exec2b (void)

虽然线程 1 使用 std::cout,线程 2 使用 m_ssMagic,但“main”(线程 0)只是等待连接。

连接协调线程完成,通常大约在同一时间。 Main(线程 0)然后 cout 的 m_ssMagic 内容。

//...
// main thread context:
case 2: // one parameter: 2 threads each runs 1/2 of tests
{  // use two new instances
    T431_t  t431a(MaxDualCoreMS); // lower test sequence
    T431_t  t431b(MaxDualCoreMS); // upper test sequence

    // 2 additional threads started here
    std::thread tA (&T431_t::exec2a, &t431a);
    std::thread tB (&T431_t::exec2b, &t431b);

    // 2 join's - thread main (0) waits for each to complete
    tA.join();
    tB.join();

    // tA outputs directly to std::cout
    // tB captured output to T431_t::m_ssMagic. 

    // both thread 1 and 2 have completed, so ok to:
    std::cout << t431b.ssMagicShow() << std::endl;

    retVal = 0;
} break;

为了完整,这里是

std::string ssMagicShow() { return (m_ssMagic.str()); }

总结

我先写了单线程应用程序。完成这项工作后,我寻找了一种“简单”的方式来利用我桌面上的第二个内核。

作为我第一次重构的一部分,我 a) 添加了初始化为 &std::cout 的“std::ostream m_so”,并且 b) 找到了 std::cout 的所有用途。其中大部分我只是用“*m_so”替换。然后我 c) 确认我没有破坏单线程解决方案。很简单,第一次尝试就成功了。

随后的努力实现了命令行“双线程”选项。

我认为,如果预算允许,这种方法将适用于我的下一个桌面。


从 OOP 的角度来看,这项工作是有效的,因为 std::ostream 位于 std::cout 和 std::stringstream 的类层次结构中。因此

 "std::cout is-a std::ostream", 

"std::stringstream is-a std::ostream". 

因此 m_so 可以指向任一派生类的实例,并为任一目的地提供虚拟方法“ostream-access”。

【讨论】:

    猜你喜欢
    • 2015-03-02
    • 2016-11-02
    • 1970-01-01
    • 2011-05-12
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多