在c++中打印字符串到临时流对象

Printing a string to a temporary stream object in C++

本文关键字:对象 字符串 c++ 打印      更新时间:2023-10-16

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

ostringstream() << "PARTY DOWN!" << endl;
现在,在你说:"但是扎克,那些代码完全没有价值!"对象在行尾被销毁,你怎么知道它是否做了什么?",听我说完。我没有尝试使用普通的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的constness,将其强制转换为ostream,并让ostream操作符做它们的事情。请随意在评论中把它撕成碎片,并警告我我介绍的所有可怕的潜在bug !

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

重载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)"
}

你不想实现stringstream。您需要实现一个basic_streambuf,它写入到您的特殊字符串中。

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

毕竟,stringstream只是一个iostream,附加了一个basic_stringbuf。

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

#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== Command: ./LogClass

= = 16197 = =

党失望!0

党失望!1

党失望!2

党失望!3

党失望!4

党失望!5

党失望!6

党失望!7

党失望!8

党失望!9

= = 16197 = =

==16197== HEAP:

==16197== in use at exit: 0 bytes in 0 blocks

==16197==总堆使用率:40 allos, 40 free, 7,350 bytes分配

= = 16197 = =

==16197==释放所有堆块——不可能出现泄漏

= = 16197 = =

==16197==对于检测到和抑制的错误计数,使用-v

重新运行:

==16197== ERROR SUMMARY: 0 errors from 0 contexts (suppression: 15 from 8)

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

这是我使用的一个变通方法:

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

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