3

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

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

现在,如果链中的第一个值是上面的常量字符串,则调用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";
}
4

2 回答 2

5

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

在您的代码中,表达式Trace() << "xyz"只能调用成员函数,因为Trace()创建了一个临时数组,它不能绑定到非成员operator<<函数的第一个参数,因为这些函数将第一个参数作为std::ostream&非常量引用。因此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 World\n" << 100 << "\nBye\n";

输出:

Hello World
100
Bye

现场演示

于 2013-01-17T14:39:35.030 回答
5

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

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

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

以这种方式调用任何成员函数,例如Tracer() << leftor Tracer() << flush,因此将引用“转换”为左值引用是非常安全的。

如果你碰巧有一个兼容 C++11 的编译器,标准库甚至包含一个operator<<(basic_ostream<...>&&,为你做这件事的工具。在这种情况下,您不再需要解决方法。

于 2013-01-17T14:40:12.940 回答