【问题标题】:The first string argument to a stringstream is saved as a pointer/garbage [duplicate]字符串流的第一个字符串参数保存为指针/垃圾[重复]
【发布时间】:2012-03-26 15:20:25
【问题描述】:

可能重复:
Printing a string to a temporary stream object in C++
std::ostringstream printing the address of the c-string instead of its content

我正在尝试使用 stringstream 构建一个字符串,就像您使用 cout 一样。这类似于日志记录类。我遇到的问题是,如果

    // class I use to print out the stream
    class StreamWriter
    {
    public:
        StreamWriter()
        {}

        ~StreamWriter()
        {
            std::string myMessage = m_stringstream.str();
            std::cout << myMessage << std::endl;
        }

        std::stringstream m_stringstream;
    };

    // macro for simplification
    #define OSRDEBUG (StreamWriter().m_stringstream)

    // actual use
    OSRDEBUG << "Hello " << "my " << "name is Pris " << 123456;

    // output
    0x8054480my name is Pris 123456
    0x8054480my name is Pris 123456
    0x8054480my name is Pris 123456
    0x8054480my name is Pris 123456

任何人都可以阐明发生了什么,以及我该如何解决这个问题?

编辑: 以下更改(除了 padiablo 的示例)同样有效,保持类的析构函数与宏的使用。

    // class I use to print out the stream
    class StreamWriter
    {
    public:
        StreamWriter()
        {   m_stringstream = new std::stringstream;   }

        ~StreamWriter()
        {
            std::string myMessage = m_stringstream.str();
            std::cout << myMessage << std::endl;
            delete m_stringstream;
        }

        std::stringstream * m_stringstream;
    };

    // macro for simplication
    #define OSRDEBUG *(StreamWriter().m_stringstream)

但最初的问题仍然存在,因为它看起来应该可以工作......而且我认为知道何时将其放入生产质量代码可能很重要。

【问题讨论】:

  • 有什么理由不使用#define OSRDEBUG std::cout
  • @Xeo:啊,不错的收获,就在几周前,我也问了我的问题。不过,自从 Nawaz 很好地解释了根本原因并给出了解决方法后,我已经将其“重定向”到了我的。

标签: c++ pointers stringstream memory-address


【解决方案1】:

这似乎是您实例化对象的方式的结果。我仍在调查(同时您可能会得到更好的答案),但显式实例化对象工作正常:

#include <iostream>
#include <sstream>

class StreamWriter {
public:
    StreamWriter() {}
    ~StreamWriter() { std::cout << m_stringstream.str() << std::endl; }
    std::stringstream m_stringstream;
};

int main (void) {
    StreamWriter *sw = new StreamWriter();
    sw->m_stringstream << "Hello " << "my " << "name is Pris ";
    delete sw;
    return 0;
}

在堆栈上实例化也是如此:

int main (void) {
    StreamWriter sw;
    sw.m_stringstream << "Hello " << "my " << "name is Pris ";
    return 0;
}

这两个都打印出您所期望的,但您的代码的以下简化没有:

int main (void) {
    StreamWriter().m_stringstream << "Hello " << "my " << "name is Pris ";
    return 0;
}

如果您只是在寻求解决方案,您可能可以通过:

#define ORSDEBUG StreamWriter sw; sw.m_stringstream

并确保您对命令进行范围保护,以便它不会尝试创建多个 sw,并且它会在正确的时间被销毁,与您最初的尝试相同:

{ ORSDEBUG  << "Hello " << "my " << "name is Pris"; }

【讨论】:

  • 感谢您抽出宝贵时间进行调查!我正在通过宏进行实例化,因为我需要调用析构函数。在我给出的示例代码中,我只是将消息的内容打印到 std::cout,但我打算在某个时刻用字符串在析构函数中做其他事情,这就是我以这种方式设置它的原因。
【解决方案2】:

我已经尝试了几个替代方案,我唯一得到的工作是这样的:

#define OSRDEBUG(s)                 \
    do                              \
    {                               \
        StreamWriter writer;        \
        writer.m_stringstream << s; \
    } while (0)

OSRDEBUG("Hello " << "my " << "name is Pris " << 123456);

我个人多次将上述构造用于我自己的日志记录解决方案,并看到其他人也这样做过。

我还不足以知道为什么你的例子不起作用,但我想这与临时工活得不够长有关。

【讨论】:

    【解决方案3】:

    老实说,我不明白到底发生了什么(这与您的 StreamWriter 实例是临时的有关),但我看到了与 GCC 和 MSVC 中描述的 paxdiablo 相同的效果。

    但是,这里有一些可以解决问题的方法。将以下帮助程序添加到您的 StreamWriter 类:

        ostream& get_ostream() {
            return m_stringstream;
        }
    

    并将宏更改为:

    #define OSRDEBUG (StreamWriter().get_ostream())
    

    【讨论】:

    • 或者只做StreamWriter().flush()。您已经有一个返回 ostream&amp; 的函数,否则它本质上是无操作的。没有理由写一个新的。
    【解决方案4】:

    问题确实是流是临时的。

    在 C++11 之前,不存在将右值引用作为第一个参数的非成员 operator&lt;&lt; 重载(也就是允许写入临时流)。

    因此,唯一有效的operator&lt;&lt; 重载是成员,因为所有非成员重载都采用非const 引用,因此不会绑定到临时对象。这些非成员重载之一是负责打印 C 字符串(又名char const*)的重载。这是成员重载之一:

    basic_ostream<Ch, Traits>& operator<<(void* p);
    

    猜猜你的字符串字面量转换成什么。 :)

    在第一个字符串之后,您会从对 operator&lt;&lt; 的调用中获得一个正常的引用,这将允许非成员重载是可行的。

    【讨论】:

    • +1 击败我。
    • @Jon:我不敢相信这个问题已经“无人回答”了一个多小时。 :)
    • @xeo:我也不能,但我有幸之前问过这个问题;)
    • @Matthieu:我感觉以前有人问过这个问题,我想我记得是你的。哦,好吧,好像你的也是重复的! :)
    • 太好了——谢谢你的回答!
    猜你喜欢
    • 2015-06-15
    • 2016-09-29
    • 2015-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-07
    • 1970-01-01
    • 2013-08-03
    相关资源
    最近更新 更多