qDebug 學習小結

dbzhang800發表於2011-06-01

在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 及 原始碼

 

相關文章