ostrstream将常量字符串解释为指针

l4m*_*mpi 3 c++ strstream

我在清理旧C/C++应用程序的调试宏时遇到了这个问题:我们有一个继承自的Tracer类ostrstream(我知道它自C++ 98以来已被弃用,但这个应用程序是在1998年编写的!)我们使用它像这样:

Tracer() << "some" << " message" << " here";
Run Code Online (Sandbox Code Playgroud)

现在,如果链中的第一个值是上面的常量字符串,则调用ostrstream::str()Tracer 的结果(在析构函数中完成,将结果插入到队列中)包含指向此字符串的指针的十六进制表示,而不是文本.因此,上述陈述会产生类似的结果"0x401a37 message here".旧宏不会发生这种情况,因为它们总是有一个长(线程ID)作为现在已被删除的第一个值.

使用gdb进入它会显示,对于第一次插入,这将调用operator<<(void const*)ostrstream,而后续插入调用operator<< <...>(basic_ostream<...>&, char const*)(为了可读性而移除了模板).

有人可以解释这种行为吗?什么是一个干净的方法来解决这个问题?我找到了一个简单的解决方法,它<< left用作第一个参数 - 这样安全吗?有没有更好的方法来做到这一点?

这是一个最小化的例子:

#include <strstream>
#include <iostream>

using namespace std;

class Trace : public ostrstream {
    public:
        Trace();
        virtual ~Trace();
};

Trace::Trace() : ostrstream() {}
Trace::~Trace() {
    static_cast< ostrstream& >(*this) <<ends;
    char * text = ostrstream::str();
    cout << "MESSAGE: "<< text <<endl;
    delete[] text;
}

int main(){
    Trace() << "some" << " text" << " here";
    Trace() << left << "some" << " text" << " here";
    Trace() << 123 << " text" << " here";
}
Run Code Online (Sandbox Code Playgroud)

Naw*_*waz 5

首先要注意的是operator<<,const char*作为参数的是非成员函数.并且存在一个以void const*成为参数的成员函数.

在您的代码中,表达式Trace() << "xyz"只能调用成员函数,因为它Trace()创建了一个temporay,它不能绑定到非成员operator<<函数的第一个参数,因为这些函数将第一个参数作为std::ostream&非const引用.所以Trace() << "xyz"解析为成员 operator<<这需要void*作为参数,它打印的地址!


我的建议:

  • 不要从流类继承(无论如何std::ostrstream都不推荐使用).
  • 而是在流类和重载上编写一个简单的包装器 operator<<

这是一个例子:

#include <sstream> //for std::ostringstream

struct Trace
{
   std::ostringstream ss;

   template<typename T>
   Trace& operator << (T const & data)
   {
        ss << data;
        return *this;
   }
   ~Trace()
   {
       std::cout << ss.str() << std::endl;
   }
};
Run Code Online (Sandbox Code Playgroud)

现在您可以将其用作:

Trace() << "Hello World\n" << 100 << "\nBye\n";
Run Code Online (Sandbox Code Playgroud)

输出:

Hello World
100
Bye
Run Code Online (Sandbox Code Playgroud)

现场演示


Bo *_*son 5

它以这种方式工作,因为它Tracer()是一个临时(rvalue),不能绑定到非const引用operator<<(basic_ostream<...>&,.

但是,您可以调用成员函数operator<<(void const*),因为这不需要左值.

成员函数然后返回到该流对象的引用可以在主叫的下一个可使用operator<<的序列中

以这种方式调用任何成员函数,Tracer() << left或者Tracer() << flush,因此将引用"转换"为左值引用是非常安全的.

如果您碰巧拥有符合C++ 11标准的编译器,那么标准库甚至包含一个operator<<(basic_ostream<...>&&,为您执行此操作的库.在这种情况下,您不再需要解决方法.