6

我有一种特殊类型的 ostringstream,我试图将文本作为临时对象输出,但我遇到了一些麻烦。需要明确的是,这基本上是我想要做的:

ostringstream() << "PARTY DOWN!" << endl;

现在在你说:“但是 Zack,那段代码完全没有价值!对象在行尾被销毁,你怎么知道它是否做了什么?”,听我说完。我不会尝试使用普通的 ostringstreams 来执行此操作,而是使用派生类,其中析构函数实际上为数据提供了退出对象的路径。所以实际上,它看起来更像这样:

specialstringstream() << "PARTY DOWN!" << endl;

其中 specialstringstream 有一个析构函数,可以将文本转储到别处。

我不会详细说明我为什么要这样做。您必须相信我,这对我需要做的事情是有意义的,并且它非常适合现有的巨大代码库。

问题是:当我这样做时,一切都编译并运行,但我得到一个指针地址打印到我的输出而不是“PARTY DOWN!” 细绳。我确定发生这种情况是因为编译器选择执行流输出的运算符是 ostream& operator<< (const void* val),不是ostream& operator<< (ostream& out, const char* s )

我对为什么有一个模糊的想法,但我不知道如何解决它。我该怎么做才能让 char*s 打印到字符串流的临时实例中?

这是展示该行为的 SpecialStringStream 对象的简短版本:

class SpecialStringStream : public ostringstream
{
  public:
    SpecialStringStream(ostream* regularStream)
    {
      regularStream_ = regularStream;
    }

    ~SpecialStringStream()
    {
      if (regularStream_ != NULL)
        (*regularStream_) << str(); 
    }

  private:
    ostream* regularStream_;
};

当我执行以下操作时:SpecialStringStream(someStreamPtr) << "PARTY DOWN!" << endl;,我在输出中得到一个指针地址,如“00444D60”,而不是消息。

编辑:由于我对用户来说太新了,无法回答自己的问题,因此感谢所有回复,这就是我所决定的。

我想出了以下解决方案,它适用于 Visual C++ 8 和我需要的所有其他编译器。我创建了一个模板运算符,它基本上去除了 const SpecialStringStream 的常量,将其转换为 ostream,并让 ostream 运算符完成他们的工作。请随意在评论中将其撕成碎片,并警告我我已经介绍的所有可怕的潜在错误!

template <class T>
std::ostream& operator<<(const SpecialStringStream &o, T msg)
{
    return static_cast<std::ostream&>(const_cast<SpecialStringStream&>(o)) << msg;
}
4

4 回答 4

4

重载ostream& operator<< (ostream& out, const char*)是不可行的,因为您的临时不会绑定到非常量引用ostream&。除了声明一个局部变量并使用它之外,您实际上无能为力(因为您不能将右值转换为左值):

{
  specialstringstream ss;
  ss << "Hello world" << std::endl; // OK, can bind to lvalue
}

可能的解决方案:您可以声明另一个接受右值引用的重载:

std::ostream & operator<<(specialstringstream && o, const char * s)
{
  return o << s; // note: *not* "std::move(o)"
}
于 2011-11-04T18:21:09.720 回答
2

您不想实现 stringstream。您想实现一个写入您的特殊字符串的 basic_streambuf。

流本身负责格式化和类似的功能;流缓冲区负责最终成为数据的接收器。

毕竟,所有字符串流都是一个 iostream,附加了一个 basic_stringbuf。

于 2011-11-04T18:44:29.627 回答
2

也许有一些更好的方法,但我想出了另一个解决方案:

#include <iostream>
#include <sstream>

class LogClass
{ 
    template <typename T>
    friend const LogClass& operator << (const LogClass& lc, const T& v);
public:
    LogClass()
        : str(new std::ostringstream())
        , refCount(new int(1))
    {}

    LogClass(const LogClass& other)
        : str(other.str)
    {
        ++(*refCount);
    }

    ~LogClass()
    {
        --(*refCount);
        if (!*refCount)
        {
            delete refCount;
            std::cout << str->str() << std::endl;
            delete str;
        }
    }
private:
    mutable std::ostringstream *str;
    int *refCount;

    LogClass& operator = (const LogClass&);
};

template <typename T>
const LogClass& operator << (const LogClass& lc, const T& v)
{
    (*lc.str) << v;
    return lc;
}

int main(int , char**)
{
    for (size_t i = 0; i < 10 ; ++i)
    {
        LogClass() << "PARTY DOWN! " << i;
    }
}

用 valgrind 运行它:

valgrind --tool=memcheck --leak-check=full ./LogClass

==16197== Memcheck,内存错误检测器

==16197== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.

==16197== 使用 Valgrind-3.7.0.SVN 和 LibVEX;使用 -h 重新运行以获取版权信息

==16197== 命令:./LogClass

==16197==

聚会!0

聚会!1

聚会!2

聚会!3

聚会!4

聚会!5

聚会!6

聚会!7

聚会!8

聚会!9

==16197==

==16197== 堆摘要:

==16197== 在退出时使用:0 个块中的 0 个字节

==16197== 总堆使用量:40 分配,40 释放,7,350 字节分配

==16197==

==16197== 所有堆块都被释放——不可能有泄漏

==16197==

==16197== 对于检测到和抑制的错误计数,重新运行:-v

==16197== 错误摘要:0 个上下文中的 0 个错误(抑制:8 个中的 15 个)

这就是我想要的,但它不是线程安全的。使用 boost 中的 shared_ptr 来实现。

于 2011-12-20T12:40:16.523 回答
0

这是我正在使用的解决方法:

#define STRM2STR(x) (dynamic_cast<std::ostringstream &>(std::ostringstream() << std::dec << x).str())

插入 std::dec 将导致调用 ostream::operator<< (ios_base& (*pf)(ios_base&)) ,它返回可用的 ostream &

于 2013-06-17T16:11:52.120 回答