在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; }