• qDebug 学习小结


     

    在qtcentre中看到有网友问这样一个问题:

    Why this doesn't work? 
    qDebug() << "Test" << std::endl;
    • 第一反应:这两个东西本来就不能这样搭配使用啊。
    • 第二反应:额,如何解释这个问题呢?还真不知道
    • 第三反应:...

    std::cout<<std::endl;

    在Qt中用了二三年C++了,还真没想过C++中的这么一个简单的语句是怎么工作的:

    • 只知道std::endl等价于换行+flush
    • 再一想,却不知道endl是什么东西了

    函数指针

    std::endl 是一个模板函数的函数指针

    template <class charT, class traits>
      basic_ostream<charT,traits>& endl ( basic_ostream<charT,traits>& os );

    看看GCC中的具体实现

    template<typename _CharT, typename _Traits>
        inline basic_ostream<_CharT, _Traits>& 
        endl(basic_ostream<_CharT, _Traits>& __os)
        { return flush(__os.put(__os.widen('/n'))); }

    操作符重载

    <<   原本是移位操作符,毫无疑问这儿是重载以后的:

    typedef basic_ostream<_CharT, _Traits> __ostream_type;
          
    __ostream_type&
    operator<<(__ostream_type& (*__pf)(__ostream_type&))
    {
        return __pf(*this);
    }

    这样以来我们就是它是怎么工作的了。也知道下面两个也就等价了

    std::cout<<std::endl;
    std::endl(std::cout);

    带参数的操纵符?

    std::endl、std::flush、std::hex等等都称为 Manipulator。如前所述,他们都是函数指针。

    除此之外,还有一些带参数的 Manipulator,比如:

    std::cout<<std::setw(8);

    这又是神马东西(反正不像是函数指针了)?

    • 看看GCC的头文件:
    inline _Setw
    setw(int __n)
    { return { __n }; }
    • 函数返回值是一个结构体_Setw 的对象

    struct _Setw { int _M_n; };
    • 同样需要操作符<< 重载

    template<typename _CharT, typename _Traits>
    inline basic_ostream<_CharT, _Traits>& 
    operator<<(basic_ostream<_CharT, _Traits>& __os, _Setw __f)
    {
      __os.width(__f._M_n);
      return __os; 
    }

    qDebug

    • 首先qDebug有两个重载的函数:
    void qDebug(const char *, ...);
    QDebug qDebug();
    • 后者需要包含QDebug这个头文件才能使用,大家都比较熟悉了。
    • 其次,如果定义了宏  QT_NO_DEBUG_OUTPUT ,qDebug将什么都不做。这是因为:

    #ifdef QT_NO_DEBUG_OUTPUT
    #  define qDebug while(false)qDebug
    #endif

    qInstallMsgHandler()

    qInstallMsgHandler 设置的是一个静态的函数指针变量(handler):

    typedef void (*QtMsgHandler)(QtMsgType, const char *);
    static QtMsgHandler handler = 0;
    
    QtMsgHandler qInstallMsgHandler(QtMsgHandler h)
    {
        QtMsgHandler old = handler;
        handler = h;
    #if defined(Q_OS_WIN) && defined(QT_BUILD_CORE_LIB)
        if (!handler && usingWinMain)
            handler = qWinMsgHandler;
    #endif
        return old;
    }

    qDebug 和这个东西是怎么联系上的?

    • 首先,参数可变的 qDebug 调用了 qt_message 函数:
    void qDebug(const char *msg, ...)
    {
        va_list ap;
        va_start(ap, msg); // use variable arg list
        qt_message(QtDebugMsg, msg, ap);
        va_end(ap);
    }
    • 然后借助QString做个转换,调用qt_message_output函数:
    static void qt_message(QtMsgType msgType, const char *msg, va_list ap)
    {
        QByteArray buf;
        if (msg)
            buf = QString().vsprintf(msg, ap).toLocal8Bit();
        qt_message_output(msgType, buf.constData());
    }
    • 在这个函数中
      • 如果安装有MsgHandler(即静态的函数指针变量handler非0),则使用

      • 如果未安装,则输出到标准出错(注意:qDebug之所以换行是这儿引入的!)
    void qt_message_output(QtMsgType msgType, const char *buf)
    {
        if (handler) {
            (*handler)(msgType, buf);
        } else {
            fprintf(stderr, "%s/n", buf);
            fflush(stderr);
        }
    ...

    QDebug

    我们似乎很少 (fix me)直接使用这个类,一般都是通过qDebug()生成一个对象来使用

    QDebug qDebug() { return QDebug(QtDebugMsg); }

    有意思的一点:这样使用时,只有当QDebug析构时,才会将内容输出(看到前面提到的qt_message_output了吧?):

    inline ~QDebug() {
        if (!--stream->ref) {
            if(stream->message_output) {
                qt_message_output(stream->type, stream->buffer.toLocal8Bit().data());
            }
            delete stream;
        }
    }

    流操作符

    同一开始提到的C++标准库中的流操作符一样,在这儿我们也可以使用不带参数的和带参数的流操作符(注意endl和std::endl的区别):

    qDebug()<<qSetFieldWidth(8)<<800<<endl;
    • 首先,肯定有相应的重载操作符:
    class QDebug
    {
    ...
        inline QDebug &operator<<(QTextStreamFunction f) {
            stream->ts << f;
            return *this;
        }
    
        inline QDebug &operator<<(QTextStreamManipulator m)
        { stream->ts << m; return *this; }
    • 然后有相应的函数指针类型 和 类类型
    typedef QTextStream & (*QTextStreamFunction)(QTextStream &);
    
    class QTextStreamManipulator
    {
    ...
    };

    QDebug::space()等

    QDebug的三个成员函数看Manual总觉得晕乎乎的,看代码就很直观了

    class QDebug
    {
    ...
        inline QDebug &space() { stream->space = true; stream->ts << ' '; return *this; }
        inline QDebug &nospace() { stream->space = false; return *this; }
        inline QDebug &maybeSpace() { if (stream->space) stream->ts << ' '; return *this; }

     

  • 相关阅读:
    vue学习指南:第六篇(详细)
    Android Stuido代码混淆
    Android 2018最新的三方库
    使用Git上传代码到Github仓库
    android展示注册进度效果源码
    JPTabBar 详细介绍
    利用HTML5和echarts开发大数据展示及大屏炫酷统计系统
    Android Studio Git 分支使用实践
    RecyclerView的Item的单击事件
    Android实用代码
  • 原文地址:https://www.cnblogs.com/lvdongjie/p/4759426.html
Copyright © 2020-2023  润新知