今天学习创建Dialog,但是刚开始一脸懵逼,C++不太熟啊,通过学习总结下:
项目结构如图:
主要说说hellodialog.h和.cpp文件。
hellodialog.h如下:
1 #ifndef HELLODIALOG_H 2 #define HELLODIALOG_H 3 4 #include <QDialog> 5 6 //namespace Ui { 7 //class hellodialog; 8 //} 9 10 class hellodialog : public QDialog 11 { 12 Q_OBJECT 13 public: 14 explicit hellodialog(QWidget*parent = 0); 15 ~hellodialog(){}; //带“{}”是定义,有无分号都行。如果是声明,则不带“{}”,但必须有分号。 16 17 signals: 18 19 public slots: 20 21 private: 22 // Ui::hellodialog *ui; 23 }; 24 25 #endif // HELLODIALOG_H
1. 第10行,用到了继承,hellodialog继承了QDialog基类。14行中的 explicit 主要用于 "修饰 "构造函数. 指明构造函数只能显示使用,目的是为了防止不必要的隐式转化.具体解释会另起一篇解释。 而且声明了了构造函数hellodialog(QWidget*parent = 0);15行的析构函数在基类中是虚函数,这里在后面加上“{}”,不需要释放内存之类的必要操作,就不用再.cpp中给其定义了。不然会报错。或者在.cpp加入实际代码,给析构函数定义。
下边是hellodialog.cpp:
1 #include "hellodialog.h" 2 hellodialog::hellodialog(QWidget *parent) : QDialog(parent) 3 { 4 //ui = new Ui::hellodialog; 5 //ui->setupUi(this); 6 } 7 8 //hellodialog::~hellodialog() 9 //{ 10 // delete ui; 11 //}
2. 主要说说第二行:有两点,1作用域解析运算符::,hellodialog::hellodialog(QWidget *parent)
作用域符号::的前面一般是类名称,后面一般是该类的成员名称,C++为例避免不同的类有名称相同的成员而采用作用域的方式进行区分
如:A,B表示两个类,在A,B中都有成员member。那么
A::member就表示类A中的成员member
B::member就表示类B中的成员member
3. 基类初始化----向基类传递参数
如下面代码:
1 class Base 2 { 3 public: 4 Base(int SomeNumber) 5 { 6 // Do something with SomeNumber 7 } 8 }; 9 10 class Derived:public Base 11 { 12 public: 13 Derived():Base(25) 14 { 15 //derived class constructor code 16 } 17 };
4. Qt中的namespce Ui{}
可以看到在hellodialog.h中和ui_hellodialog.h中
1 #ifndef HELLODIALOG_H 2 #define HELLODIALOG_H 3 4 #include <QDialog> 5 6 namespace Ui { //这个时前置声明 不是定义 好处下边说。 7 class HelloDialog; 8 } 9 10 class HelloDialog : public QDialog 11 { 12 Q_OBJECT 13 public: 14 explicit HelloDialog(QWidget *parent = 0); 15 16 signals: 17 18 public slots: 19 20 private: 21 Ui::HelloDialog *ui; 22 }; 23 24 #endif // HELLODIALOG_H
/******************************************************************************** ** Form generated from reading UI file 'hellodialog.ui' ** ** Created by: Qt User Interface Compiler version 4.8.6 ** ** WARNING! All changes made in this file will be lost when recompiling UI file! ********************************************************************************/ #ifndef UI_HELLODIALOG_H #define UI_HELLODIALOG_H #include <QtCore/QVariant> #include <QtGui/QAction> #include <QtGui/QApplication> #include <QtGui/QButtonGroup> #include <QtGui/QDialog> #include <QtGui/QHeaderView> #include <QtGui/QLabel> QT_BEGIN_NAMESPACE class Ui_HelloDialog { public: QLabel *label; void setupUi(QDialog *HelloDialog) { if (HelloDialog->objectName().isEmpty()) HelloDialog->setObjectName(QString::fromUtf8("HelloDialog")); HelloDialog->resize(400, 300); label = new QLabel(HelloDialog); label->setObjectName(QString::fromUtf8("label")); label->setGeometry(QRect(120, 120, 151, 31)); retranslateUi(HelloDialog); QMetaObject::connectSlotsByName(HelloDialog); } // setupUi void retranslateUi(QDialog *HelloDialog) { HelloDialog->setWindowTitle(QApplication::translate("HelloDialog", "Dialog", 0, QApplication::UnicodeUTF8)); label->setText(QApplication::translate("HelloDialog", "Hello World! \344\275\240\345\245\275Qt\357\274\201", 0, QApplication::UnicodeUTF8)); } // retranslateUi }; namespace Ui { class HelloDialog: public Ui_HelloDialog {}; } // namespace Ui QT_END_NAMESPACE #endif // UI_HELLODIALOG_H
其中 #define QT_BEGIN_NAMESPACE namespce QT_NAMESPCE{
#define QT_END_NAMESPACE }
这个在49-51行定义了名称空间Ui。而.h中的只是声明而已。
命名空间知识:
命名空间可以包含的内容有:变量(可初始化),常量,数(定义或声明),类,模板,命名空间(在一个命名空间中又定义一个命名空间,即嵌套的命名空间)。
在一个命名空间中,声明一个int a; 那么a就实实在在的会存在,不只是一个声明。
1 namespce test1 2 { 3 int a; 4 } 5 namespce test1 6 { 7 int a; 8 }
在同一个文件中两次定义test1,编译会报错。很明显,我们对a进行了两次定义,但是当我们把第7行换做别的内容,比如 int b; class 。。。就可以通过,在同一文件定义了一样的命名空间,但内容不一向,相当于拓展。
hellodialog.h中的
namespce Ui{
class Hello_dialog;//这里只是声明
}
顺便说一下,当命名空间里只有一些变量,但有想做声明的话,可以封装在类里。
说先说说Pimp机制:
--------------------------------Pimpl机制开始--------------------------------------------------
1.简介
这个机制是Private Implementation的缩写,我们常常听到诸如“不要改动你的公有接口”这样的建议,所以我们一般都会修改私有接口,但是这会导致包含该头文件的所有源文件都要重新编译,这会是个麻烦事儿。Pimpl机制,顾名思义,将实现私有化,力图使得头文件对改变不透明。
2.机制分析
首先,我们先看看不使用这个机制的一个实现:
1 // MyBase.h 2 class MyBase { 3 public: 4 int foo(); 5 }; 6 7 // MyDerived.h 8 #include "MyBase.h" 9 class MyDerived : public MyBase { 10 public: 11 int bar(); 12 };
假设你现在希望在MyBase.h中加入一个新的private和protected成员函数,那么MyDerived和所有包含MyBase.h的源文件都需要重新编译。在一个大工程中,这样的修改可能导致重新编译时间的激增。你可以使用Doxygen或者SciTools看看头文件依赖。
一般来说,不在头文件中包含头文件是一个比较好的习惯,但是这也不能完全消除修改MyBase.h带来的重新编译代价。有没有一个机制可以使得对私有接口做修改时我们可以减小重新编译的代价。
在Pimpl机制中,我们使用前置声明一个Impl类,并将这个类的一个指针实例放入主类中,如下:
1 // MyClass.h 2 class MyClassImpl; // forward declaration 3 class MyClass { 4 public: 5 MyClass(); 6 ~MyClass(); 7 int foo(); 8 private: 9 MyClassImpl *m_pImpl; 10 };
现在,除非我们修改MyClass的公有接口,否则这个头文件是不会被修改了。
在一个既定平台上,任何指针的大小都是相同的。之所以分为X*,Y*这些各种各样的指针,主要是提供一个高层的抽象语义,即该指针到底指向的是那个类的对象,并且,也给编译器一个指示,从而能够正确的对用户进行的操作(如调用X的成员函数)决议并检查。但是,如果从运行期的角度来说,每种指针都只不过是个32位的长整型(如果在64位机器上则是64位,根据当前硬件而定)。
正由于m_pImpL是个指针,所以这里MyclassImpl的二进制信息(sizeof(MyClass)等)不会被耦合到MyClass的使用接口上去,也就是说,当用户”new MyClass”或”MyClass myclass1”的时候,编译器生成的代码中不会掺杂MyClassImpl的任何信息,并且当用户使用MyClass的时候,使用的是Myclass的接口,也与MyClassImpl无关,从而MyClassImpl被这个指针彻底的与用户隔绝开来。只有MyClass知道并能够操作m_piml成员指向的MyClassImpl对象。
Pimpl就是为了减少各个源文件之间的联系。
------------------------Pimpl机制结束----------------------------------------------------------------
下边说说qt中这个前置声明的好处:
在ui_hellodialog.h代码中都是在Qt设计模式中,设计的代码生成,如何让代码被使用呢?
//ui_hellodialog.h
1 class Ui_HelloDialog{// ui_类名 2 public: 3 定义 按键,标签之类的。 4 void setupUi(基类的指针参数){ 5 dosomething with object; 6 } 7 }; 8 namespce Ui{ 9 class HelloDialog :public Ui_HelloDialog{}; 10 //定义一个命名空间,在其中定义一个类,一个继承Ui_类的派生类。 11 }
有两种方法使用这些代码,
第一种:
1 // hellodilaog.h 2 #include "ui_hellodialog.h" 3 class mydialog 4 { 5 Ui_HelloDialog dlg; 6 }; 7 // mydialog.cpp 8 #include "My.h" 9 // 实现mydialog
但是这样存在问题, 如果ui_hellodialog.h文件的内容被改变, 不但mydialog.cpp会被重新编译,
所有包含mydialog.h的文件也都会被重新编译。而且这确实是一个问题: 在设计中确实经常被拖来拖去。
第二种:引入Pimpl机制,使用前置声明 前置声明一个Ui::HelloDialog类。我们使用前置声明一个Impl类,并将这个类的一个指针实例放入主类中。 hellodialog.h中的第6-8,和20-22行。
然后用户使用 HelloDialog.h 头文件以及 HelloDialog类。该文件被修改的频率就会低很多很多。无论是将designer上的界面元素拖来拖去, 还是添加删除, hellodialog.h文件的内容——HelloDialog类的定义——都不会改变。
然后用户可以使用这个HelloDialog了:
#include "hellodialog.h"
class My
{
HelloDialog dlg;
};
不过Qt自动生成的类名和命名空间中定义的类名是一样的,都是HelloDialog,前边可以加作用域解析符区分,也可以更改。
在qt4中使用了继承的方式来使用designer创建的窗体,也就是同时继承QDialog和UI_Dialog。
而在Qt Creator自动创建的项目中,使用了组合的方式来使用Designer创建的窗体,就是集成QDialog,而将UI_HelloDialog作为一个成员变量来使用,也就是
private:
Ui::HelloDialog *ui;
在前一种方式中,你可以在继承类中直接使用UI_Dialog上的组件。在后一种方式中,你要使用ui->XXX的方式使用UI_Dialog上的组件。