宏开关
宏头文件
test.h
1 | #ifndef TEST_H |
使用宏处
1 | #include "test.h" |
不使用LOG_PRINTF时,注释#define TEST_DEBUG_ON
头文件多次包含问题
c/c++ 头文件多次包含,会出现重复定义的问题。
避免头文件多次包含的方式
- 使用宏保护
1
2
3
4#ifndef TEST_H
#define TEST_H
#endif
测试 TEST_H 预处理器变量是否未定义。如果 SALESITEM_H 未定义,那么 #ifndef 测试成功,跟在#ifndef 后面的所有行都被执行,直到发现 #endif。相反,如果 TEST_H 已定义,那么 #ifndef 指示测试为假,该指示和 #endif 指示间的代码都被忽略。
- 使用
#pragma once
c++中#pragma once
是编译器相关的,有的编译器支持,有的编译器不支持,具体情况请查看编译器API文档。
QML学习之路
qml简介
QML
是一种描述性的脚本语言,文件格式以.qml
结尾。语法格式非常像CSS
,但又支持javacript
形式的编程控制。它结合了QtDesigner UI
和QtScript
的优点。QtDesigner
可以设计出.ui
界面文件,但是不支持和Qt原生C++代码的交互。QtScript
可以和Qt
原生代码进行交互,但是有一个缺点,如果要在脚本中创建一个继承于QObject
的图形对象非常不方便,只能在Qt
代码中创建图形对象,然后从 QtScript
中进行访问。而QML
可以在脚本里创建图形对象,并且支持各种图形特效,以及状态机等,同时又能跟Qt
写的C++
代码进行方便的交互,使用起来非常方便。
可以使用Qt自带的工具qmlscene.exe
直接运行qml文件查看qml界面效果
qml的基本元素
Item(基础元素对象)是所有可视化元素的基础对象,所有其它的可视化元素都继承自Item。它自身不会有任何绘制操作,但是定义了所有可视化元素共有的属性:
分组 | 属性 |
---|---|
Geometry(几何属性) | x,y(坐标)定义了元素左上角的位置,width,height(长和宽)定义元素的显示范围,z(堆叠次序)定义元素之间的重叠顺序。 |
Layout handling(布局操作) | anchors(锚定),包括左(left),右(right),上(top),下(bottom),水平与垂直居中(vertical center,horizontal center),与margins(间距)一起定义了元素与其它元素之间的位置关系。 |
Key handlikng(按键操作) | 附加属性key(按键)和keyNavigation(按键定位)属性来控制按键操作,处理输入焦点(focus)可用操作。 |
Transformation(转换) | 缩放(scale)和rotate(旋转)转换,通用的x,y,z属性列表转换(transform),旋转基点设置(transformOrigin)。 |
Visual(可视化) | 不透明度(opacity)控制透明度,visible(是否可见)控制元素是否显示,clip(裁剪)用来限制元素边界的绘制,smooth(平滑)用来提高渲染质量。 |
State definition(状态定义) | states(状态列表属性)提供了元素当前所支持的状态列表,当前属性的改变也可以使用transitions(转变)属性列表来定义状态转变动画。 |
可以通过Qt creator
的调试状态查看元素的属性
新建一个qml工程
在 文件 新建文件或项目 对话框中,选择 其他项目,然后选择 Qt Quick UI Prototype
例如以下Qml代码的属性1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import QtQuick 2.8
import QtQuick.Window 2.2
Window {
id: root
property int blurRadius: 0
Image {
id: pole
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
source: "images/pole.png"
}
MouseArea {
anchors.fill: parent
onPressed: {
wheel.rotation += 90
root.blurRadius = 16
}
onReleased: {
root.blurRadius = 0
}
}
}启动调试
- 属性列表
- image的属性
鼠标区域的属性
qml必须导入
QtQuick
,导入方式import QtQuick 2.8
使用Qt已有的组件需要导入相应的模块
如使用Window
导入import QtQuick.Window 2.2
模块,2.2是Qt模块的版本号
qml注释符和C++一样
1 | /* 这是一个多行注释 |
qml的布局
要使用QML进行界面的布局,首先需要理解QML元素的层次结构。QML的层次结构很简单,是一个树形结构,最外层必须有一个根元素,根元素里面可以嵌套一个或多个子元素,子元素里面还可以包含子元素。如果用图形画出来的话大概是这个样子。
使用anchor
进行布局
qml与C++的交互
qml与C++交互的桥梁是 QML引擎和元对象系统
qml
跟C++
的交互方式主要有以下几种:
以直接在C++应用程序中加载qml文件,拿到界面各元素的指针,修改界面属性 (在C++中,加载qml文件)
可以将C++对象expose 到qml中,然后在qml文件中访问该对象的属性或调用对象的方法 (在qml中,访问C++对象并调用C++方法)
可以自定义C++类,把该类注册给qml类型系统,然后可以像其他内置类型那样在qml中使用 (在qml中,使用导入的C++ 类型)
qml与C++结合的优势:
- 界面与逻辑完美分离:用QML来定义界面,用C++来实现界面的响应逻辑。
- 通常的做法是,当用户在界面上操作的时候,我们从qml文件里面调用C++的响应函数。
- 应用MVC方便:用QML来描绘界面(View),用C++代码来实现Model 和 Controller。
- 自定义控件容易:可以用C++来自定义自己的QML类型, 然后将它应用于我们的应用程序中。
- 可以在程序运行时访问或修改QML对象的属性进而改变界面的显示
一些网址
多态
多态的条件
- 必须存在继承关系
- 继承关系必须有同名虚函数
- 存在基类类型的指针或者引用,通过该指针或引用调用虚函数。
虚函数声明
- 纯虚函数声明如下:
virtual void funtion()=0
纯虚函数一定没有定义,纯虚函数用来规范派生类的行为,即接口。包含纯虚函数的类是抽象类,抽象类不能定义实例,但可以声明指向实现该抽象类的具体类的指针或引用。 - 虚函数声明如下:
virtual ReturnType FunctionName(Parameter)
虚函数必须实现。
不建议用父类指针指向子类,尤其是父类析构函数没有声明为虚函数,采用父类指针,其生存周期结束时会自动调用父类的析构函数,而不会调用子类析构函数,子类未完全析构,造成内存泄漏。
实现多态父类析构函数定义为虚函数
- 构造函数顺序:先执行父类的构造函数,再执行子类的构造函数
析构函数顺序:先执行子类的析构函数,再执行父类的析构函数
父类析构函数没有定义为virtual时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class mParent
{
public:
mParent()
{
cout << "p gouzhao" << endl;
}
~mParent()
{
cout << "p xigou" << endl;
}
virtual void print()
{
cout << "this is parent" << endl;
}
};
子类A1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class A : public mParent
{
public:
A()
{
cout << "A gouxhao" << endl;
}
~A()
{
cout << "A xigou" << endl;
}
virtual void print()
{
cout << "this is A" << endl;
}
};
使用多态1
2
3parent * p = new A;
p->F();
delete p;
结果是:
此时delete p
没有使用子类A的析构函数,如果A里有裸指针,则会造成内存泄漏
- 父类析构函数定义为virtual时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class mParent
{
public:
mParent()
{
cout << "p gouzhao" << endl;
}
virtual ~mParent()
{
cout << "p xigou" << endl;
}
virtual void print()
{
cout << "this is parent" << endl;
}
};
结果是:
此时delete p
使用子类A的析构函数.
构造函数不能声明为虚函数的原因
- 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象 的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类,无法确定
- 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初 始化,将无法进行。
虚函数的意思就是开启动态绑定,程序会根据对象的动态类型来选择要调用的方法。然而在构造函数运行的时候,这个对象的动态类型还不完整,没有办法确定它到底是什么类型,故构造函数不能动态绑定。
静态多态
在编译时实现多态
动态多态
在运行时实现多态