时间:2016年9月3日 天气:晴:sunny:
Author:冬之晓:angry:
Email: 347916416@qq.com
MyAppearance:
今天是周六,一早就起来去了一趟超市,买了一堆吃的东西! 这样我就可以这里两天不用动在宿舍待着啦,连吃饭都可以躺在床上解决啦,想想真的是太好啦!!!
今天主要想复习并学习一下Qt的绘图功能,简单的二维绘图主要使用QPainter
进行绘图,只要在任意QWidget
内部重写paintEvent(QPaintEvent *)
方法,并且内部使用QPainter
进行绘图,就可以绘制一个图形,例如:
void SimpleExampleWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setPen(Qt::blue);
painter.setFont(QFont("Arial", 30));
painter.drawText(rect(), Qt::AlignCenter, "Qt");
}
这种绘制方法的优点是比较简单方便,缺点就是对于无法实现复制的绘图,特别是需要操作绘图中几个到几百个项的图形元素,拖拽图形之类的复杂操作就难以实现了。因此专业一点的绘图方式是使用QGraphicsScene
充当场景和一些由QGraphicsItem
的子类充当项。
QGraphicsScene
是一个图形项的集合。一个场景一共有三层:背景层、项层和前景层。
QGraphicsView
是一个窗口部件,可以用来显示场景,在需要时提供滚动条,以及影响场景绘制方式的变化能力。有利于支持放缩。旋转等。默认情况下,QGraphicsView
使用Qt内置的二维图形引擎绘图,但是这可以改变,在其创建完成后调用setViewport()
改为使用OpenGL窗口部件。
条款20:宁以pass-by-reference-to-const替换pass-by-value
缺省的情况下,C++是以by value方式传递对象至函数。函数实参都是以实际实参的复件为初始值,而调用端获得的亦是函数返回值的一个复件。这些复件系由对象的copy构造函数产出,这可能使得pass by value成为昂贵的操作。考虑下面的继承体系:
classPerson{
public:
Person();
vitual~Person();
private:
std::stringname;
std::stringaddress;
};
classStudent:publicPerson{
public:
Student();
virtual~Student();
private:
std::stringschoolName;
std::stringschoolAddress;
};
现在有一个调用函数validateStudent,要调用一个Student实参并返回它是否有效?
bool validateStudent(Student s); //函数以by value的方式接受学生
Student plato; //柏拉图,苏格拉底的学生
bool platoIsOK = validateStudent(plato); //调用函数
上面的例子中先后调用了Person的构造函数和Student的构造函数,而每个类中又包含两个string对象,所以总共会调用6次构造函数(4个string构造、Person构造和Student构造,注意这里的构造包括构造函数和拷贝构造函数),相应也会调用6次析构函数(注意当有派生关系存在时,基类的析构函数应该声明成虚的,这个在条款七中提到过)。
面对这种情况,使用pass-by-reference-to-const,传引用调用可以减少构造函数的调用外,还可以防止对象切割。Person&只是告诉编译器它保存的地址对应的内容是一个Person类型的,它会优先把这个内容往Person上去套,但如果里面有虚函数,即使用了virtual关键字,那么编译的时候就会往类中安插一个虚指针,这个虚指针的指向将在运行时决定,这就是多态的机制了,它会调用实际传入的那个对象的虚函数,而不是基类对象的虚函数。
如果没有接触过多态,上面说的可能就会有难度,其实也可以简单的理解成,如果不加引用或指针,那么形参就会复制实参,但形参是基类的,它没有实参多出来的那部分,所以它就不能复制了,只能丢弃了;但如果加了引用或指针,那么无论是什么类型的,都是保存实参的地址,地址是个好东西啊,它不会发生切割,不会丢弃。
请记住:
-
尽量以pass by reference to const替换pass by value。前者通常比较高效,并可避免切割的问题。
-
以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass by value更加适当。
设计模式(五)————装饰模式
装饰模式:动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活
比如要出门,给自己穿上各种衣服,就可以使用一个服装类:
#ifndef PERSON
#define PERSON
#include <QString>
#include <QTextStream>
#include <QtDebug>
class Person
{
QString name;
public:
Person(){} //子类不想用构造函数,父类必须实现无参数构造函数(如果父类没有构造函数时,默认编译器产生的构造函数即可!)
Person(QString n)
{
name = n;
}
virtual void show()
{
qDebug() <<"装扮的"<<name<<":";
}
virtual ~Person(){}
};
class Finery:public Person
{
protected:
Person *component = nullptr; //内含一个用来装饰的类,使用方式见main函数!
public:
//装饰
void Decorate(Person *com)
{
component = com;
}
void show() override //把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此当需要执行特殊行为时,客户代码
//就可以在运行时根据需要有选择的、按顺序的使用装饰功能包装对象!!
{
if(component != nullptr)
component->show();
}
};
class Tshirt final:public Finery
{
public:
void show() override
{
Finery::show();
qDebug() <<"大T恤";
}
};
class Skirt final:public Finery
{
public:
void show() override
{
Finery::show();
qDebug() <<"裙子";
}
};
class stocking final:public Finery
{
public:
void show() override
{
Finery::show();
qDebug() <<"长袜";
}
};
#endif // PERSON
然后在主函数中,就可以随意的调用
#include "person.h"
int main(int argc, char *argv[])
{
Person *a = new Person("小红");
Finery *t = new Tshirt();
Finery *s = new Skirt();
Finery *st = new stocking();
t->Decorate(a);
s->Decorate(t);
st->Decorate(s);
st->show();
return 0;
}
这样就实现了简单的装饰模式。