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

ostrstream interprets constant string as pointer

本文关键字:指针 解释 字符串 常量 Ostrstream      更新时间:2023-10-16

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

Tracer() << "some" << " message" << " here";

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

使用gdb进行深入研究表明,对于第一次插入,它调用了strstream上的operator<<(void const*),而随后的插入调用了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";
}

这样做是因为Tracer()是一个临时的(右值),不能绑定到operator<<(basic_ostream<...>&,中的非const引用。

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

成员函数然后返回一个对流对象的引用,可以用于调用序列中的下一个operator<<

以这种方式调用任何成员函数,如Tracer() << leftTracer() << flush,从而将引用"转换"为左值引用是非常安全的。

如果你碰巧有一个c++ 11兼容的编译器,标准库甚至包含一个operator<<(basic_ostream<...>&&,来为你做这些。在这种情况下,你就不需要变通了。

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

在你的代码中,表达式Trace() << "xyz"只能调用成员函数,因为Trace()创建了一个临时,它不能绑定到非成员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;
   }
};

现在你可以这样使用:

Trace() << "Hello Worldn" << 100 << "nByen";
输出:

Hello World
100
Bye

现场演示