当前位置: 首页 > news >正文

『 QT 』Qt初识

文章目录

    • Qt介绍
    • IDE选择
    • 环境配置简述
    • QtSDK 工具简述
    • 项目的创建
    • 空白项目代码解释
    • from file 文件
    • .pro 工程文件
    • 创建简单的 HelloWorld
    • 对象树
    • 为什么不推荐将QT控件对象创建在栈上
    • 自定义控件类的创建
    • 析构函数的顺序
    • QT中的乱码问题


Qt介绍

Qt 是一个 跨平台的 C++ 图形用户界面应用程序框架, 通常用于开发桌面, 移动和嵌入式系统的应用程序;

Qt支持多种操作系统, 包括 Windows, Linux, macOS, AndroidiOS 等;

同时 Qt 提供了丰富的工具和库, 帮助开发者快速构建高质量的图形用户界面(GUI)和后台逻辑;

Qt 因版本不同, 可能会在语法上出现些许差异, 该系列博客采用Qt6;


IDE选择

Qt常用的工具一般为QtCreator, vscode, visual studio, Eclipse等工具, 但通常情况下可以使用QtCreator作为初始的开发工具, 主要因为该工具集成与配置简单, 界面友好, 即配即用, 同时Qt CreatorQt 的官方集成开发环境 (IDE), 其他的工具(IDE)可能涉及到需要额外配置, 在学习的初期可能增加整体成本;


环境配置简述

Qt的环境配置通常需要配置三个部分, 分别为C++编译器, Qt SDK, Qt的集成开发环境;

  • 编译器

    使用C++编写Qt程序时需要配置编译器, 通常Qt所使用的编译器为gcc/g++, cl.exe等;

  • Qt SDK

    所谓的SDK就是软件开发工具包, 通常情况下, Windows版本的Qt SDK已经内置了C++的编译器(mingw, 可以理解为Windows版本下的gcc/g++);

    除此之外可以使用其他的编译器, 但是需要根据需求来进行额外的配置;

  • 集成开发环境

    所谓的集成开发环境就是在上文中所提到的QtCreator, vscode, visual studio, Eclipse等环境, 初学时推荐使用QtCreator;

通常情况下, 若是使用QtCreator作为开发的IDE时只需要安装QtSDK, 安装QtSDK后会默认安装编译器和QtCreator IDE;

Qt的安装步骤此处省略;


QtSDK 工具简述

通常安装了QtSDK后在Qt目录中找到这些工具;

主要为Qt Creator, Assistant, Designer, Linguist;

  • Assistant - Qt助手

    这是Qt的官方离线文档;

  • Designer - Qt设计师

    这是一个Qt的图形化界面的开发工具, 可以通过点击或者拖拽来添加控件, 而通常情况下QtCreator将会内置使用这个工具达到"所见即所得";

  • Linguist - Qt语言家

    这是QtSDK所提供的一个用于适配国际化的翻译工具, 支持 Qt C++ 和 Qt Quick 应用程序的国际化;

    开发者, 翻译者和发布者可以使用 Qt Linguist 来创建, 管理和使用翻译文件, 以支持多语言应用程序;

  • Qt

    该程序将会自动打开一个命令行, 在该命令行中可以直接使用QtSDK内置的命令对项目进行构建;

  • Qt Creator

    QtSDK自带的原生的集成开发环境IDE, 上述工具通常内置在Qt Creator中;

    通常使用QtCreator来完成具体的项目开发;


项目的创建

通常情况下构建项目的顺序为上图所示;

在项目一栏中存在多个选项;

通常根据不同的需求点击不同的选项, 右侧的子页面也会根据项目一栏的选项进行更变, 如Application(Qt)选项为创建的项目为一个默认的项目, 使用C++进行编码;

而若是选择Application(Qt for Python)则表示所创建的项目是一个通过Python编码的Qt项目(此处不进行介绍);

当选择Application(Qt)时其右侧子页面将会存在下列几个模板选项;

  • Qt Widgets Application

    Qt Widgets Application是基于传统的控件(Widgets)来构建用户界面GUI的应用程序;

    其中Widgets是像按钮(QButton), 文本框(QLineEdit), 列表(QListView)等标准元素;

    主要使用C++代码来创建和操作界面, 也可通过配合QtDesigner工具通过拖拽方式设计XML格式的.ui文件;

    其主要特点为如下:

    1. 原生外观

      在不同的操作系统上会使用该系统的原生风格;

    2. 功能强大复杂

      适合开发较为复杂的, 传统的桌面应用程序, 如工业控制软件, 数据库管理系统, IDE(如 QtCreator本身就为Widgets所写)等;

    3. 重量级

      该模板下的控件通常较"重", 更适合鼠标键盘交互;

  • Qt Console Application

    Qt Console Application是一个没有GUI的命令行程序, 通常运行在终端或者控制台窗口中;

    使用C++进行编写, 可以使用Qt核心模块中的非GUI的功能, 如网络(Qt Network), 数据库(Qt SQL), XML处理, 文件操作等;

    该模板的主要特点为如下:

    1. 无界面

      通过输入输出文本与用户交互;

    2. 轻量

      编译后的程序体积小, 不需要加载GUI库;

    3. 适配自动化

      该模板非常适合编写后台服务, 自动化脚本, 数据处理工具等;

  • Qt Quick Application

    Qt Quick Application是基于下一代 Qt (Qt Quick)来构建现代, 流畅, 动效丰富的用户界面的应用程序, 其核心是QML(一种声明式JavaScript-like语言)来描述界面, 通常使用C++JavaScript来处理逻辑;

    通常使用QML(用于UI和交互)与JavaScript/C++配合编写;

    在此不进行过多解释;

  • Qt Quick Application (compat)

    该模板为从Qt5Qt6迁移项目而提供的兼容性模板;

    该模板与标准的Qt Quick Application类似, 但它会使用一个为了兼容Qt5代码而存在的Qt5Compat模块;

    在此不进行过多解释;

通常情况下编写传统桌面软件时使用Widgets即可;

当模板选择好后进行下一步, 为项目创建路径与名称(项目路径中不能使用带有中文的路径, 否则可能会出现项目构建失败等问题);

当项目名称和路径创建好后需要选择Qt项目的构建工具;

构建工具通常有qmake, CMake以及Qbs;

  • qmake

    qmake是QT原生支持的项目构建工具, 针对于QT项目, 能够提供较为简单且快速的项目构建;

  • cmake

    cmake是大多数项目的构建工具, 同时也能够用来构建QT项目工具;

  • Qbs

    一款新型的QT项目构建工具, 但使用人数较少, 目前已经暂停维护;

通常QT项目的构建工具可以选择除Qbs以外的工具, 当项目较为简单时使用qmake;

当项目较为复杂且超出qmake限制时采用cmake;

在使用Qt Creator创建项目时会自动生成一些代码, 通常情况下所生成的代码会包含一个类, 而在Base class中可以选择自动生成类的父类;

三个选项分别为QMainWindow, QWidgetQDialog三个类;

  • QMainWindow

    该父类通常表示一个完整的应用程序窗口;

    包含菜单栏, 工具栏, 状态栏等其他组件;

  • QWidget

    该父类表示一个基础的窗口类, 是QMainWindowQDialog的父类, 其为轻量级的通用容器, 无预置菜单栏, 工具栏等组件, 可作为独立的窗口或嵌套组件;

    通常用于自定义对话框, 设计复杂界面中的子面板, 绘制自定义控件的基础;

  • QDialog

    是一个专用对话框基类, 其支持模态/非模态弹窗支持, 同时自带对话框按钮布局, 如(OK/Cancel);

上文的这些类都是QT中的内置类, 而QT的内置类都是以Q作为开头的;

当选择好Base class后可对Class name进行命名, 通常情况下, 此处的 class name和文件名, 路径名是相关联的, 但通常情况下, 这种关联并不是强制的, 当然推荐所创建的类名与文件名相关联是一个比较好的做法, 可以对项目进行一个更好的管理;

Details部分中涉及到几个文件, 分别为.h头文件, cpp源文件和一个.uixml结构文件, 其中.ui文件是一个可以展示当前样式的文件, 以达到所见即所得, 可通过QTCreater中的代码或是Designer的鼠标直接拖动来完成应用程序的设计;

Detail部分结束后是Translation页面, 在该页面下可以选择设置语言翻译来完成应用程序的一个国际化的翻译, 若是没有需求可直接点击下一步;

在构建套组页面中选择基于哪个编译器的QtSDK来构建后续代码, 通常使用该页面下默认所选择的QtSDK即可;

在汇总页面中可根据需求判断项目是否添加到版本控制系统;

最后点击完成即可构建一个新的QT项目;

创建完项目后可直接对项目进行运行;

该窗口即为一个Widget项目的初始状态;

Ps: 在Qt项目中尽可能不要出现中文, 否则可能会出现项目构建失败等问题!!

空白项目代码解释

当项目生成后将会有四个文件, 分别为widget.h文件, widget.cpp文件与一个main.cpp文件;

  • main.cpp

    主函数所在源文件;

    #include "widget.h"#include <QApplication>int main(int argc, char *argv[])
    {QApplication a(argc, argv);Widget w;w.show();return a.exec();
    }
    
    • QApplication

      一个QT项目中必须存在一个QApplication对象, 通常情况下, 这个对象用来接收命令行参数;

      管理程序的事件循环(如鼠标事件, 键盘事等事件)并提供基本的GUI设置和管理功能;

    • Widget w

      Widget是在项目构建时所选择的一个框架类型, 在这里实例化了一个自定义的窗口类, 一般情况下默认直接使用栈进行内存分配, 当程序结束时将会自动销毁;

      这个类继承于QWidget类;

    • w.show()

      这是一个展示函数, 主要的功能是让窗口进行显示, 本质上新创建的窗口默认是隐藏的, show()成员函数能够让改框架的窗口进行展示;

      相关的方法还有:

      • w.show()

        显示窗口(非模态);

      • w.showMaximized()

        最大化显示;

      • w.showFullScreen()

        全屏显示;

      • w.hide()

        隐藏窗口;

    • return a.exec()

      这里是启动Qt的事件循环, 程序运行并进行exec()成员函数的执行后, 程序会再次进入无限循环, 等待并处理事件(如鼠标点击, 键盘输出, 窗口重绘等);

      当接收到退出信号时(如关闭窗口或是程序异常)时将会终止循环, 并返回应用程序的退出码;

      该处的exec与Linux中的exec系统调用接口无关;

    其大致的工作流为如下:

    1. 程序启动

      执行main()函数;

    2. 初始化

      创建QApplication对象并设置应用程序环境;

    3. 创建界面

      实例化主窗口类;

    4. 显示界面

      调用show()使窗口可见;

    5. 事件处理

      进入事件循环, 响应用户交互;

    6. 程序退出

      用户关闭窗口, 事件循环结束, 程序退出;

其次widget.hwidget.cpp两个文件共同组成了一个Widget类;

  • widget.h

    该文件描述了widget类的声明;

    #ifndef WIDGET_H
    #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
    namespace Ui {
    class Widget;
    }
    QT_END_NAMESPACEclass Widget : public QWidget
    {Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:Ui::Widget *ui;
    };
    #endif // WIDGET_H
    

    上述代码为widget类的声明, 其中使用了声明卫士以确保该头文件只被包含一次以防止头文件重复包含问题;

    同时引用了头文件QWidget;

    在代码中可以看到, Widget类继承自基类QWidget, 该基类则来自QWidget头文件, 所继承的类则为在创建项目时所选择的基类;

    其中Q_OBJECTQTSdk中内置的宏, 通常是为 信号 所准备的;

    在该段代码中存在这样一行代码:

        Widget(QWidget *parent = nullptr);
    

    在QT中引入了一个名为"对象树"的机制;

    当创建一个QT的对象时需要将对象挂在对象树上, 本质上就是指定其父节点;

    private成员中存在一个UI::Widget *ui, 该对象与.ui文件密切相关;

  • widget.cpp

    #include "widget.h"
    #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
    {ui->setupUi(this);
    }Widget::~Widget()
    {delete ui;
    }
    

    其中widget.hwidget对象的声明头文件, ui_widget.h头文件则是form fileqmake生成的头文件;

    在这段代码中存在:

    ui->setupUi(this);
    

    即为将form file生成的界面和当前的widget进行关联;

    最后的析构函数则为对已用资源进行释放;


from file 文件

在QTCreator中的文件管理中存在一个目录为from file(界面文件), 在当前form file中存在一个widget.ui文件;

直接通过鼠标双击的方式打开文件将会间接使用Designer工具以设计模式打开该文件;

这是一个图形化的界面编辑器;

在左侧中存在QT内置的空间, 可直接采用拖动的方式将控件直接拖到应用程序内部进行可视化设计;

在左侧中的编辑按钮可以将当前的设计模式改为编辑模式;

相应的如果在该处直接用拖动等方式进行设计时, 对应的在其编辑模式中的代码也会出现相应的变化;

同时也可以在程序窗口的右侧为控件设置对应的属性;

Qt中使用的XML文件本质就是描述程序的界面, 进一步的项目构建工具(cmake, qmake)将会调用相关的工具, 依据XML文件生成一些Cpp代码, 从而构建完整的界面;


.pro 工程文件

在项目目录中将会存在一个.pro文件, 该文件为项目构建工具选择qmake时的工程文件, 也是qmake构建项目的重要依据;

在项目结构中存在一个build目录, 该目录下通常存放一些项目构成的中间件;

在该目录下存在一个.h文件, 其文件名通常为ui_xxx.h文件, 该文件即为.uiXML文件所生成的.h文件;

.h文件中存在一个成员方法, 为void setupUi(QWidget *Widget), 该成员方法即为widget.cpp中所调用的方法;

在该目录下的Debug/Release目录中的.exe文件即为该项目构建后的最终的可执行文件;


创建简单的 HelloWorld

  • 可视化设计

    在项目中点击.ui文件打开设计模式, 拖动控件(此处举例lable标签)进窗口并编辑显示文本;

    从上文中提到的项目结构中的ui_xxx.h文件中能观察到其新增的控件;

  • 代码编辑方式编写

    代码编写的方式通常是去编辑Widget.cpp这样的文件对程序页面进行一个设计;

    通常将构造界面的代码放到Widget/MainWindow的构造函数中;

    QT中每个类都有一个对应同名的头文件, 若是需要使用某些控件需要显式的去包含这些头文件;

    此处同样以Label标签为例;

    在引入头文件中可能会出现两个与控件名相同的头文件;

    一般使用<QLabel>的这种风格(c++98标准成立后规定包含头文件使用<cstdio>的这种风格来代替<stdio.h>的风格);

        QLabel* label = new QLabel(this); // 传入this指针表示为当前label对象指定一个父对象// QLabel label; // 若是设置对象树可能导致非法访问引出的段错误问题
    

    在创建对象可以使用在栈上创建, 也可以使用在堆上创建, 但一般情况下更推荐在堆上创建对象(与对象树有关);

    设置完QLabel对象后可为该对象设置文本属性, 通常使用QLabel::setText()函数设置对象的文本;

     QString text() const void setText(const QString &);
    

    其中该函数所传入的参数为const QString&类型的参数;

    c++98标准前的STL容器并不标准好用, 因此QT为了适配自身的项目体系重新设计了一套容器, QString类型即为QT内部的字符串类型;

    除了QString以外还有QList, QVector, QMap等容器;

    在新版的QT中, 可以使用C++原生的容器(C++ STL越发完善, 并存在标准), 也可以使用QT内置容器;

    一般情况下QT中的容器的头文件已经被其他类内置包含了, 因此一般不需要再显式包含;

    Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
    {ui->setupUi(this);QLabel* label = new QLabel(this); // 表示为当前label对象指定一个父对象label->setText("Hello world!!");
    }
    

    构建运行程序;

    从这个例子中可以看到, 在设计QT控件时并没有为控件显式调用delete来释放内存, 本质上是因为该控件被连接到了对象树上;

    当程序运行时(如关闭窗口), 其根节点会通过QObject类的析构函数进行清理, 这个清理工作是一个递归的操作, 即QObject去调用它的析构函数, 这个操作会一直向下遍历他的子节点, 让他们分别去调用子节点的析构函数, 最终从子节点向上去释放内存(深度优先);

    通常对象树是由QObject中的Children列表所管理的;


对象树

在QT中存在一种主要的结构被称为对象树, 这棵对象树主要来管理其子节点的生命周期;

对象树的结构如上图所示, 本质上是一个节点的实例, 每个节点都存放一个指针, 指针指向一个ChildrenList列表, 列表是每个节点指针;

/* 示例 */
class QObject{private:QObject *_parent; = nullptr; // 指向父节点的指针QList<QObject *> _childrens; // 存储子对象的链表指针QString objectName;// ...public:~QObject(){qDeleteAll(_childrens); // 删除所有子对象, 遍历子对象对子对象进行内存释放, 在释放过程中子对象将会调用他们自己的析构函数完成深度优先遍历的内存释放}void setParent(QObject *parent){if(this->_parent){this -> _parent -> _childrens.removeAll(this); // 从原父节点中移除this -> _parent = parent;if(_parent){_parent->_childrens.append(this); // 设置当前节点的父节点指针关联后 将当前父节点中的子节点列表中添加自己(this指针);}}const QList<QObject*>& children() const {return _children;}QObject* parent() const {return _parent; // 返回父节点}}
};

对象树的核心在于QObject类中的析构函数和setParent()方法;

  • 析构函数~QObject

    这是自动销毁子对象的起点, 当父对象析构时, 他会自动析构所有其所有子对象, 从而形成级联删除, 以确保所有子对象被正确释放;

    /* 示例 */
    QObject::~QObject()
    {// 1. 核心:删除所有子对象(对象树的基石)QObjectPrivate *d = d_func();// 如果存在子对象列表且不为空if (!d->_children.isEmpty()) {// 遍历并删除所有子对象(注意:从后往前删除更高效)for (int i = d->_children.size() - 1; i >= 0; --i) {QObject *child = d->_children.at(i);// 确保子对象存在且当前对象是其父对象if (child && child->_parent() == this) {// 关键:设置子对象的父对象为nullptr,然后删除它child->setParent(nullptr);  // 避免重复删除delete child;}}}// 2. 从父对象的子对象列表中移除自己if (d->_parent) {d->_parent->d_func()->removeChild(this);}// 3. 清理其他资源(信号槽连接、事件过滤器、定时器等)// ... 其他清理代码 ...
    }
    

当然成功析构需要确保有将新控件交由父节点管理, 即调用setParent()函数来设置新控件的父节点指针;

  • setParent()

     void setParent(QObject *parent){if(this->_parent){this -> _parent -> _childrens.removeAll(this); // 从原父节点中移除this -> _parent = parent;if(_parent){_parent->_childrens.append(this); // 设置当前节点的父节点指针关联后 将当前父节点中的子节点列表中添加自己(this指针);}}
    

    setParent()的大致结构如该段代码为例, 如果当前节点存在父节点, 那么将该节点的父节点与当前节点去除关联, 随后再与新的父节点新增关联;


为什么不推荐将QT控件对象创建在栈上

从上文得知, Qt控件对象在创建过程中, 如果涉及到父子关系的话, 需要将控件对象的指针挂在对象树上, 也方便内存管理;

同时在Qt上通过代码创建控件对象时通常是在当前QT框架的构造函数上编写的, 这会造成两个问题:

  • 控件对象生命周期端

    框架的构造函数结束时, 对应控件的生命周期也将结束;

  • 访问野指针

    由于控件的生命周期结束, 但只是表示该指针位置对应的空间不可用(地址失效), 不代表与对象树关联的地址也被清除, 对应的已经传入的指针会成为野指针, 对野指针进行访问时可能出现段错误;

#include "widget.h"
#include "ui_widget.h"
#include <QLabel>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QLabel label(this);label.setText("hello world");
}Widget::~Widget()
{delete ui;
}

如这段代码为例, 当项目构建并运行后标签并不会展出, 本质上是因为标签的生命周期在框架的构造函数结束后也跟着结束了, 并且在这段代码中, 如果label并不是标签而是其他子控件, 那么label将会成为其他子控件的父节点, 那么将会出现对非法地址进行访问;


自定义控件类的创建

在一个QTCreator项目中可以在左上角添加新的控件类c++ class;

添加后可选择是否将该类添加至项目中;

将新class添加至项目后, 项目将会自动生成两个文件;

示例:

在项目中创建了一个名为MyLabel的类, 该类继承于QLabel

对应的项目结构中增加了两个文件, 分别为mylabel的头文件与源文件;

mylabel.h文件中, 为class MyLabel的声明, MyLabel继承自QLabel, 这意味这MyLabel包含其父类QLabel;

而通常情况下子控件为了更好的内存管理需要将其链接至父对象的children列表中, 但当前MyLabel只是继承于QLabel, 自身并没有能力链接到对象树上, 因此需要接收并将parent指针传进MyLabel中并在MyLabel中的初始化列表中调用其父类QLabel的构造函数;

MyLabel::MyLabel(QWidget* parent/* 接收父节点指针 */):QLabel(parent)/* 调用父类的构造函数完成与对象树的连接 */ {}

因此在某个框架下需要构造一个MyLabel控件时只需要传入this指针即可, this指针作为MyLabel的父节点进行传入, 随后MyLabel将会接收parent指针并在初始化列表中调用其父类QLabel的构造函数完成对象树的链接;

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);MyLabel *_mylabel = new MyLabel(this); // 构造一个QLabel对象_mylabel -> setText("This is MyLabel"); 
}

析构函数的顺序

通常情况下一个子控件对象若是传入parent指针将会把当前控件挂在对象树上;

  • widget.cpp

    #include "widget.h"
    #include "ui_widget.h"
    #include <QLabel>
    #include "mylabel.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
    {ui->setupUi(this);MyLabel *_mylabel = new MyLabel(this);_mylabel -> setText("This is MyLabel");
    }Widget::~Widget()
    {delete ui;
    }
  • mylabel.cpp

    #include "mylabel.h"
    #include <iostream>MyLabel::MyLabel(QWidget* parent):QLabel(parent) {}MyLabel::~MyLabel()
    {std::cout<<"MyLabel destroied"<<std::endl;
    }
    
  • mylabel.h

    #ifndef MYLABEL_H
    #define MYLABEL_H#include <QLabel>class MyLabel : public QLabel // 继承自QLabel
    {
    public:MyLabel(QWidget* parent); // 在自定义的控件对象的构造函数中需要传入parent// 这样才能在构造的时候将父子节点进行关联 将新控件关联到对象树中~MyLabel();
    };#endif // MYLABEL_H
    

从上文代码中可以看到, MyLabel继承自QLabel, 并且创建了一个QLabel对象, QLabel的析构函数将会打印出 "MyLabel destroied", 但在这里的widget中并没有在其析构函数中去释放MyLabel的内存, 在运行之前我们无法知道MyLabel是否被释放;

运行程序, 程序正常运行;

当点击右上角关闭按钮关闭窗口后, "MyLabel destroied"在终端中被打印;

说明MyLabel的析构函数被调用(生命周期结束所以调用析构函数), MyLabel被释放, 这就是对象树对子控件的内存管理;

当父控件被关闭后, 将会调用~QObject()析构函数, 析构函数将会遍历自身节点中所指向的ChildrenList进行delete释放内存;

被遍历删除的节点同样会调用自己的析构函数, 对自己的ChildrenList遍历释放, 形成递归, 呈深度优先的内存释放方式逐个释放内存;

这种内存释放的方式避免了空间需要手动释放内存;


QT中的乱码问题

通常情况下如果在QT中使用std::cout打印中文字符时, 可能出现乱码问题;

在QT的main函数中引入<iostream>头文件并使用std::cout打印中文字符, 重新构建并运行程序;

#include "widget.h"#include <QApplication>#include <iostream>
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();std::cout<<"你好世界"<<std::endl;return a.exec();
}

从控制台上看到虽然打印的是中文, 但出现的是乱码, 这本质上是因为编码不同所导致的, 编码问题类似于类型问题, 就如你使用double的方式对数据进行存储, 但最终使用int的方式将数据取出, 那么就会出现问题;

#include <iostream>
int main() {double n = 5.556;printf("%d\n", n);return 0;
}

这段代码运行的结果为:

文字的乱码也是相同的道理, 写入与写出采用不同的编码格式则会出现乱码, 通常情况下, Windows若是为中文, 那么其控制台采用的是GBK编码格式, 而代码输出的采用的是UTF8的格式, 而std::cout并不会自动处理编码问题, 那么在输出展示则会出现问题;

而QT中的QStringQDebug()可以自动处理编码方式;

通常推荐上述两种方式而不是使用std::cout;

#include "widget.h"
#include <QApplication>
#include <QString>
#include <QDebug>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();qDebug() << "这是qDebug";return a.exec();
}

运行结果为:

通常情况下qDebug()用来在开发过程中输出日志, 当一个项目产品已经过了Dev阶段后, 开发者可以考虑通过一些编译开关关闭qDebug()以避免日志信息展示给用户;

http://www.dtcms.com/a/424264.html

相关文章:

  • 建设部职称网站企业网站建设的背景和目的
  • 新天力:以“专精特新“之姿,勇闯北交所资本市场新征程
  • 【连载2】 MySQL 事务原理详解
  • 简单 SPI 协议 简述
  • 【2025最新】ArcGIS for JS二维底图与三维地图的切换
  • 网站为什么会出现死链国内个人网站
  • 做网站分流阿里云网站备案后
  • 婚恋网站建设项目创业计划书汕头市企业网站建设哪家好
  • 旅游门户网站建设方案如何开发wordpress主题
  • 校园网站建设的缺陷百度升级最新版本下载安装
  • 海洋网络提供网站建设eclipse做的网站
  • ENVI系列教程(十八)——高级光谱分析
  • 怎样做影视网站不侵权小白怎样建设公司网站
  • 网页制作与网站建设填空题做网站需要买什么
  • 【控制理论】#3 一阶系统与二阶系统的时域响应分析
  • 网站建设万户网络城乡建设部网站房产查询
  • 下载并安装 Kali 官方 GPG 密钥
  • Flink 有状态流处理State、Keyed State、Checkpoint、对齐/不对齐与生产实践
  • Redis String 类型全解析
  • 网站的积分系统怎么做属于seo优化范畴的是
  • spring cache(四)cache版本管理
  • 企业做网站带来的好处哪个平台打广告效果好
  • 网站代理怎么设置成都地区网站开发成本
  • 短视频网站开发金融行业网站开发
  • 网页前端做购物网站的实训报告企业建设网站的必要性
  • UIP中的psock_generator_send()的宏分析
  • pragma alloc_text的用途及支持的段列表
  • python做直播网站wordpress建站Pdf
  • 潍坊做网站好看电影网站模板下载
  • 织梦做的网站打开空白免费cms建站