扩展QDebug实现禁用跟踪时抑制消息触发QTextStream无设备错误如何解决?
问题根因
错误的核心原因是C++类构造顺序不符合你的预期:
C++中类的构造顺序严格遵循以下规则:
- 按照继承声明的顺序执行父类构造函数
- 按照类内成员声明的顺序执行成员变量构造
- 执行当前类的构造函数体
你在MyDebug的默认构造函数初始化列表中,虽然把null_string的初始化写在父类QDebug构造前面,但父类构造永远会先于子类成员变量执行,此时null_string还未初始化,你传给QDebug构造函数的null_string.get()是空指针,最终导致QTextStream绑定空设备,写入时抛出No device错误。
你之前直接使用堆上QString*测试正常,是因为你直接把new QString的返回值传给了QDebug构造,没有依赖未初始化的类成员。
解决方案
推荐使用组合而非继承的方式重新实现MyDebug,规避父类构造顺序问题,同时实现功能更稳定:
class MyDebug { public: // 禁用跟踪时的构造:输出到空QString explicit MyDebug() : m_nullStr(new QString(), &MyDebug::doDeleteLater) , m_debug(m_nullStr.get()) {} // 启用跟踪时的构造:传入系统qDebug实例 explicit MyDebug(const QDebug& debug) : m_debug(debug) {} // 拷贝构造 MyDebug(const MyDebug& other) : m_nullStr(other.m_nullStr) , m_debug(m_nullStr ? m_nullStr.get() : other.m_debug) {} // 重载<<运算符,转发到内部QDebug实例 template<typename T> MyDebug& operator<<(const T& value) { // 可以在这里加额外的过滤逻辑 m_debug << value; return *this; } // 转发QDebug的原生方法 void setVerbosity(uchar level) { m_debug.setVerbosity(qMin<uchar>(7, level)); } private: std::shared_ptr<QString> m_nullStr; QDebug m_debug; static void doDeleteLater(QString* str) { delete str; } }; // Application的trace方法不需要做修改 MyDebug Application::trace(const uchar verbosity) const { MyDebug d = this->logLevel > 6 ? MyDebug(qDebug()) : MyDebug(); d.setVerbosity(verbosity); return d; }
如果你一定要保留继承QDebug的实现,可以调整继承顺序,把字符串持有者放到第一个继承的父类,保证其先于QDebug构造:
struct StringHolder { std::shared_ptr<QString> nullStr; static void doDeleteLater(QString* obj) { delete obj; } StringHolder() : nullStr(new QString(), doDeleteLater) {} }; class MyDebug : private StringHolder, public QDebug { public: MyDebug() : QDebug(nullStr.get()) {} MyDebug(const QDebug& o) : QDebug(o) {} MyDebug(const MyDebug& o) : StringHolder(o), QDebug(o.nullStr.get()) {} };
内容的提问来源于stack exchange,提问作者Pascal
相关产品推荐
相关产品推荐

