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

qDebug 学习小结的更多相关文章

  1. flex学习小结

    接触到flex一个多月了,今天做一个学习小结.如果有知识错误或者意见不同的地方.欢迎交流指教. 画外音:先说一下,我是怎么接触到flex布局的.对于正在学习的童鞋们,我建议大家没事可以逛逛网站,看看人 ...

  2. Python 学习小结

    python 学习小结 python 简明教程 1.python 文件 #!/etc/bin/python #coding=utf-8 2.main()函数 if __name__ == '__mai ...

  3. react学习小结(生命周期- 实例化时期 - 存在期- 销毁时期)

    react学习小结   本文是我学习react的阶段性小结,如果看官你是react资深玩家,那么还请就此打住移步他处,如果你想给一些建议和指导,那么还请轻拍~ 目前团队内对react的使用非常普遍,之 ...

  4. objective-c基础教程——学习小结

    objective-c基础教程——学习小结   提纲: 简介 与C语言相比要注意的地方 objective-c高级特性 开发工具介绍(cocoa 工具包的功能,框架,源文件组织:XCode使用介绍) ...

  5. pthread多线程编程的学习小结

    pthread多线程编程的学习小结  pthread 同步3种方法: 1 mutex 2 条件变量 3 读写锁:支持多个线程同时读,或者一个线程写     程序员必上的开发者服务平台 —— DevSt ...

  6. ExtJs学习笔记之学习小结LoginDemo

    ExtJs学习小结LoginDemo 1.示例:(登录界面) <!DOCTYPE html> <html> <head> <meta charset=&quo ...

  7. 点滴的积累---J2SE学习小结

    点滴的积累---J2SE学习小结 什么是J2SE J2SE就是Java2的标准版,主要用于桌面应用软件的编程:包括那些构成Java语言核心的类.比方:数据库连接.接口定义.输入/输出.网络编程. 学习 ...

  8. (转) Parameter estimation for text analysis 暨LDA学习小结

    Reading Note : Parameter estimation for text analysis 暨LDA学习小结 原文:http://www.xperseverance.net/blogs ...

  9. dubbo学习小结

    dubbo学习小结 参考: https://blog.csdn.net/paul_wei2008/article/details/19355681 https://blog.csdn.net/liwe ...

随机推荐

  1. flash文件运动节奏的控制

    flash里面,比较难的是控制运动的节奏.参考了几个韩国网站的fla源文件,提出以下几个建议与参考. 1,flash文件里面,每秒的帧数 设置为 120,或者一个比较大的数字(90,60).普通的文件 ...

  2. iOS9 集成指纹解锁

    添加依赖库 LocalAuthentication.framework #import <LocalAuthentication/LocalAuthentication.h> // 头文件 ...

  3. 使用NPOI插件读取excel模版修改数据后保存到新目录新文件中

    添加引用: using System.IO; using NPOI.XSSF.UserModel; using NPOI.SS.UserModel; using NPOI.HSSF.UserModel ...

  4. 32.Spring-对象依赖.md

    [toc] 1.对象依赖的分类 Spring中,给对象属性赋值的方法: 构造函数 Set方法 p命名空间 自动装配 注解 1.1构造函数 构造方法通过配置文件中constructor-arg标签实现, ...

  5. C++ 性能剖析 (三):Heap Object对比 Stack (auto) Object

    通常认为,性能的改进是90 ~ 10 规则, 即10%的代码要对90%的性能问题负责.做过大型软件工程的程序员一般都知道这个概念. 然而对于软件工程师来说,有些性能问题是不可原谅的,无论它们属于10% ...

  6. windows 7下安装python+mongodb

    1. python安装 下载:http://python.org/download/ 直接双击安装,安装完后将路径加入系统环境变量path中. 2. mongodb安装 下载:http://www.m ...

  7. JavaScript 目标装配式编程(Target Assemble Programming)

    TAP概述 脚本中一切皆对象,若还以传统模式思考编程模式,那简直是对不起脚本解释器的强大支持:我们应该以最接近人类操作方式的来表达人的意图. 更接近工作实践的方式,比如游戏中,一个人物一个角色,人物的 ...

  8. underscorejs-min学习

    2.16 min 2.16.1 语法: _.min(list, [iteratee], [context]) 2.16.2 说明: 返回list中的最小值. list为集合,数组.对象.字符串或arg ...

  9. git操作(强烈推荐)

    一:Git是什么? Git是目前世界上最先进的分布式版本控制系统. 二:SVN与Git的最主要的区别? SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以 ...

  10. grails框架中在使用domain的save方法保存时保存不成功

    1.如果报错,自行根据异常查找错误,这里不说明 2.如果为报错,我遇到的就是domain中的字段属性与数据库中为同步 (1)你的domain是新的,在增加新的字段属性时未使用update更新数据库,造 ...