时间:2016年9月4日 天气:晴:sunny:
Author:冬之晓:angry:
Email: 347916416@qq.com
MyAppearance:
有一天,我察觉到自己一无所有
发现本以为幸福满载的口袋之中,事实上空无一物
因为我从未付出过任何填补的努力,所以是理所当然的
可我却连这种事都想不明白
毕竟我的人生杂乱无章、空洞乏味
然而某一天
我突然深感自己正在失去大量的时间
我跟谁都说的上话。无论是怎样的人
可是我没有好朋友。一个都没有
这究竟意味着什么,我从未考虑过
我的人生,一直平淡乏味
——«rewrite»
今天是周末,跟姥姥通话了。现在科技真是发达,以前从未想过有一天只要使用 网络,就可以免费和远在千里之外的家人视频对话,科技真的是太棒啦!
今天,主要看了一下Qt的容器,发现原来Qt的容器在进行操作的时候,既可以使用java的风格也可以使用C++的风格,同时还通过“引用计数”的方式实现了“写时复制”的功能。真的是太强大了。比如说遍历一个QList
容器,可以使用如下两个风格的方式进行:
- Java风格
QList<int> ql;
ql<<1<<2<<3;
QListIterator<int> i(ql);
while(i.hasNext())
qDebug()<<i.next();
- C++风格
QList<int>::const_iterator j = ql.constBegin();
while(j != ql.constEnd())
{
qDebug()<<*j;
++j;
}
QVariant
条款21:当必须返回对象时,别妄想返回其reference
当你理解条款20后,很可能出现一种过度使用的情况:只要看到是一个非内置类型,就去使用引用传值。例如:
class Rational
{
public:
Rational(intnumerator=0,int denominator=1); //条款24解释此函数为什么不声明为explicit
private:
int n,d; //分子numerator和分母denominator
friend Rational operator*(constRational& lhs constRational& rhs);
};
这个版本operator*以by value返回结果(一个对象),看似要付出构造和析构成本,事实上是吗?如果改用返回引用,不需付出代价。但是,引用只是个名称,代表某个既有对象。任何时候看到引用声明式,我们就应想到是它的另一个名称是什么。operator*如果返回一个reference,则一定指向一个已有Rational对象,包含两个Rational对象乘积。如下代码:
Rational a(1,2); //a=1/2;
Rational b(3,5); //b=3/5;
Ratioanal c=a*b; //c应为3/10
原本就存在“3/10”对象并不合理“,如果operator*要返回一个对象指向此数值,必须首先创建那个Ratioanl对象函数创建新对象的方法有二:在stack空间或在heap空间。根据这个策略写为operator*如下:
const Rational& operator*(constRational& lhs constRatioanl& rhs)
{
Rationalresult(lhs.n*rhs.n,lhs.d*rhs.d);//糟糕代码
returnresult;
}
采用此方法是为了避免返回对象的构造,事实上生成result时同样会调用构造函数。更重要的是返回的result是一个local对象,在函数退出前就被销毁了。事实上:任何函数如果返回一个reference指向某个local对象,都将一败涂地。(返回一个指针指向一个local对象同样如此)。既然在stack不可行,那我们考虑在heap内构造,并返回reference指向它(事实上同样不可行)
const Rational& operator*(constRational& lhs constRatioanl& rhs)
{
Rational result=new Rational(lhs.n*rhs.n,lhs.d*rhs.d);//更糟糕的代码
return*result;
}
因为分配内存以一个适当对象完成初始化动作,还是得调用构造函数。另外一个问题:谁将对new对象实施delete?即使有良好意识,但还是无法阻止内在泄漏,考虑如下代码:
Rational w,x,y,z;
w=x*y*z; //等价于operator*(operator*(x,y),z)
调用两次operator*,两次new,但无法合法进行两次delete调用。因为没有合理办法取得operator*返回的reference的指针,必然导致资源泄漏。
一个“必须返回新对象”的函数的正确写法是(返回一个新对象):
inline const Rational operator*(constRational& lhs,const Rational& rhs)
{
return Ratioanal(lhs.n*rhs.n,lhs.d*rhs.d);
}
承担operator*返回值的构造成本和析构成本,从长远来看只是为正确行为付出的一点个代价。 以上讨论总结为:当必须在“返回一个reference和返回一个Object”之间选择时,需要做的是挑出行为正确的那个。让编译器厂商为“尽可能降低成本”鞠躬尽瘁吧!
请记住:
- 绝对不要返回pointer或reference指向一个local stack对象,指向一个heap-allocated对象也不是好方法,更不能指向一个local static对象(数组),该让编译器复制对象的时候,就让它去复制!
设计模式(六)————代理模式
代理模式:为其他对象提供一种代理以控制对这个对象的访问
比如可以做一个最求女孩的类,使用一个代理来追求的话就会是这样的代码:
#ifndef PERSUITGIRL
#define PERSUITGIRL
#include <QString>
#include <QObject>
#include <QtDebug>
//被追求者类
class schoolGirl:public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
public:
explicit schoolGirl(QObject *parent = 0):_name("undefined"){}
QString name() const
{
return _name;
}
void setName(const QString & name)
{
_name = name;
}
signals:
void nameChanged(const QString & name);
private:
QString _name;
};
//定义接口:实际对象和代理的公共接口,这样实际对象需要使用的地方都可以用代理完成!
class GiveGiftInterface
{
public:
virtual void GiveDolls()=0;
virtual void GiveFlowers()=0;
virtual void GiveChocolate()=0;
virtual ~GiveGiftInterface(){}
};
//追求者类如下
class Pursuit final:public GiveGiftInterface
{
public:
explicit Pursuit(schoolGirl *mm)
{
_mm = mm;
}
~Pursuit(){delete _mm;}
void GiveDolls() override
{
qDebug() <<_mm->name()<<"送你洋娃娃!";
}
void GiveFlowers() override
{
qDebug() <<_mm->name()<<"送你鲜花!";
}
void GiveChocolate() override
{
qDebug() <<_mm->name()<<"送你巧克力!";
}
private:
schoolGirl *_mm;
};
//代理类如下
class Proxy final:public GiveGiftInterface
{
public:
explicit Proxy(schoolGirl *mm)
{
_gg = new Pursuit(mm);
}
~Proxy()
{
delete _gg;
}
void GiveDolls() override
{
_gg->GiveDolls();
}
void GiveFlowers() override
{
_gg->GiveFlowers();
}
void GiveChocolate() override
{
_gg->GiveChocolate();
}
private:
Pursuit *_gg;
};
#endif // PERSUITGIRL
然后主函数可以这样进行测试:
#include "persuitgirl.h"
int main(int argc, char * argv[])
{
schoolGirl *mm= new schoolGirl;
mm->setName("美美");
Proxy *daili = new Proxy(mm);
daili->GiveDolls();
daili->GiveChocolate();
daili->GiveFlowers();
qDebug() <<"测试完成!";
return 0;
}
这样就是一个简单的代理模式的例子。根据上面的例子我们可以看出,实际上代理模式就是有两个类具有同样的接口,然后其中代理类包含另一个类的实例,代理的接口实际上是用那个类的实例的接口来实现的,这样就隐藏了正真实现这个接口的实例。这就是代理模式的精髓所在!