Qt_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; }
参考
The C++ Standard Library: A Tutorial and Reference
http://www.cppblog.com/yindf/archive/2009/05/12/80382.html

Qt Manual 及 源码
————————————————
版权声明:本文为CSDN博主「dbzhang800」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dbzhang800/article/details/6460229

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值