逛超市

2016/9/3 宅在家

Posted by WangXiaoDong on September 3, 2016

时间:2016年9月3日 天气:晴:sunny:


Author:冬之晓:angry:
Email: 347916416@qq.com
MyAppearance: 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;
}

这样就实现了简单的装饰模式。