《C++沉思录》——类设计核查表代理类句柄类
Posted 松狮MVP
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《C++沉思录》——类设计核查表代理类句柄类相关的知识,希望对你有一定的参考价值。
《C++沉思录》集中反映C++的关键思想和编程技术,讲述如何编程,讲述为什么要这么编程,讲述程序设计的原则和方法,讲述如何思考C++编程。
一、类设计核查表
1、你的类需要一个构造函数吗?
2、你的数据成员都是私有的合理吗?
3、你的类需要一个无参的构造函数吗?
是否需要生成类对象的数组!
4、你的每一个构造函数都初始化所有的数据成员了吗?
虽然这种说法未必总是正确,但是要积极思考!
5、你的类需要析构函数吗?
6、你的类需要一个虚析构函数吗?
7、你的类需要一个拷贝构造函数吗?
8、你的类需要重载赋值运算符吗?
9、你的操作运算符能正确将对象赋给对象本身吗?
class String
{
public:
String& operator=(const String&);
private:
char* data;
};
String& String::operator=(const String& str)
{
if (&str != this && this->data != NULL)
{
//释放旧值
delete[] data;
//重新分配
this->data = new char[strlen(str.data) + 1];
//复制新值
strcpy(this->data, str.data);
}
return *this;
}
10、你的拷贝构造函数和赋值运算符的参数类型加上const了吗?
X::X(const X& x);
X& X::operator=(const X& x);
复制、赋值都不会改变原对象,所以 const;使用引用,免去值传递时的拷贝开销。
11、你的类需要重载其他关系运算符(==、!=、<、>等)吗?
12、删除数组时你记住delete [ ]了吗?
13、如果函数有引用类型参数,它们应该是const引用吗?
14、适当地声明成员函数为const了吗?
const类型对象只能调用“声明为const的成员函数”,而不能调用“非const的成员函数”。
http://blog.csdn.net/songshimvp1/article/details/50975332
二、代理类(surrogate)
代理类的作用:允许在一个容器中储存类型不同但是相互关联的对象。它允许将整个派生层次压缩在一个对象类型中。代理类是句柄类中最简单的一种。
class Vehicle
{
public:
virtual double weight() const = 0;
virtual void start() = 0;
};
class Aircraft :public Vehicle
{
};
class Helicopter :public Aircraft
{
};
对于我们要创建的Vehicle对象数组:Vehicle parking_lot [100];(1)Vehicle是一个抽象类,不能实例化对象;(2)即使有vehicle对象,把一个派生类对象赋给某个数组元素,还是会把派生类对象转换成一个vehicle对象,裁减掉所有在Vechile类中没有的成员,最终得到的并不是继承自vehicle的对象集合,仍旧是vehicle对象的集合。
第一种解决方法:这样使用数组:Vehicle* paking_lot [100]——存储指针,而不是储存对象本身。
这种方法会带来指针异常和动态内存管理的负担。另外我们还必须实现就知道要给某数组元素赋什么派生类型(静态)。
第二种解决方法:
class Vehicle
{
public:
virtual double weight() const = 0;
virtual void start() = 0;
virtual Vehicle* copy() const = 0; //虚复制————根据需要复制对象,在运行时绑定属性
virtual ~Vehicle() = 0; //虚析构
};
class Truck :public Vehicle
{
double weight() const
{
}
void start()
{
}
Vehicle* copy() const
{
return new Truck(*this); //使用vp->copy()会得到一个指针,该指针指向该对象的一个新建的副本。
}
};
class VehicleSurrogate //定义Vehicle的代理类——行为和Vehicle相似
{
public:
VehicleSurrogate(); //用来创建代理对象数组
VehicleSurrogate(const Vehicle&); //用来创建派生类对象的代理
~VehicleSurrogate();
VehicleSurrogate(const VehicleSurrogate&);
VehicleSurrogate& operator=(const VehicleSurrogate&);
//Vehicle的其他操作
double weight();
void start();
private:
Vehicle* vP; //关联
};
VehicleSurrogate::VehicleSurrogate() :vP(0)
{
}
VehicleSurrogate::VehicleSurrogate(const Vehicle& v) :vP(v.copy())
{
}
VehicleSurrogate::~VehicleSurrogate()
{
delete vP;
}
VehicleSurrogate::VehicleSurrogate(const VehicleSurrogate& v) :vP(v.vP ? v.vP->copy() : 0) //(1)注意对v.vP的非0检测;(2)虚调用v.vP->copy();
{
}
VehicleSurrogate& VehicleSurrogate::operator=(const VehicleSurrogate& v)
{
if (this != &v)
{
delete vP;
vP = (v.vP ? v.vP->copy() : 0); //(1)注意对v.vP的非0检测;(2)虚调用v.vP->copy();
}
return *this;
}
double VehicleSurrogate::weight()
{
if (vP == 0)
{
throw "empty VehicleSurrogate.Weight()";
}
return vP->weight();
}
void VehicleSurrogate::start()
{
if (vP == 0)
{
throw "empty VehicleSurrogate.start()";
}
return vP->start();
}
然后,我们这么使用数组:——通过在容器中使用代理对象,而不是对象本身!
int index = 0;
VehicleSurrogate parking_lot[1000];
Truck vT;
parking_lot[++index] = VehicleSurrogate(vT);
三、句柄类(handle)
(1)句柄要解决的问题:
上述使用代理类,我们看见了需要复制代理对象,复制消耗会很大,或者是不能轻易被复制(文件)。句柄类就是为了解决:句柄类允许在保持代理的多态行为的同时,还可以避免进行不必要的复制!
(2)句柄的简单实现——引用计数型句柄
使用句柄的原因之一就是为了避免不必要的对象复制,也就是得允许多个句柄绑定到同一个对象上,也就需要知道有多少个句柄绑定在同一个对象上,以便确定应当在何时删除对象。——使用引用计数来实现
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Point
{
public:
Point() :xVal(0), yVal(0) { }
Point(int x,int y) :xVal(x), yVal(y) { }
int getX() const
{
return xVal;
}
int getY() const
{
return yVal;
}
Point& x(int x)
{
xVal = x;
return *this;
}
Point& y(int y)
{
yVal = y;
return *this;
}
private:
int xVal;
int yVal;
};
class UPoint //该类纯粹是为了在添加引用计数后不重写Point而设计
{
friend class Handle;
private:
UPoint() :u(1) {}
UPoint(int x, int y):p(x, y),u(1)
{
}
UPoint(const Point& p0):p(p0),u(1)
{
}
private:
Point p;
int u; //引用计数——不能把引用计数放在句柄类
};
class Handle
{
public:
Handle();
Handle(int, int);
Handle(const Point&);
Handle(const Handle&);
Handle& operator=(const Handle&);
~Handle();
int getX() const;
Handle& x(int);
int getY() const;
Handle& y(int);
private:
UPoint* uP; //句柄类关联UPoint类
};
Handle::Handle() : uP(new UPoint)
{
}
Handle::Handle(int x, int y) :uP(new UPoint(x, y))
{
}
Handle::Handle(const Point& p) : uP(new UPoint(p))
{
}
Handle::Handle(const Handle& h) : uP(h.uP) //拷贝构造,这样原先的句柄和其副本都指向同一个UPoint对象
{
++uP->u; //引用计数加1,避免了复制Point
}
Handle& Handle::operator=(const Handle& h) //赋值运算符,左侧的句柄在赋值后将指向另外一个对象
{
//首先递增右侧句柄指向对象的引用计数
++h.uP->u;
//然后递减左侧句柄所指向对象的引用计数
if (--(this->uP->u) == 0)
{
delete uP;
}
//赋值
this->uP = h.uP;
return *this;
}
Handle::~Handle()
{
if (--uP->u == 0) //递减引用计数,如果引用计数减为0,就删除Upoint对象
{
delete uP;
}
}
//以下“存数据成员”方式是“值语义”,也就是所改动的那个UPoint对象不影响其他的UPoint对象
//{
// Handle h1(1, 2);
// Handle h2 = h1;
// h2.x(3);
// int n = h1.x(); //输出1
//}
//这就需要保证所改动的那个UPoint对象不能同时被其他任何Handle所引用
Handle& Handle::x(int x0)
{
if (this->uP->u != 1) //?没理解?——写时复制,只有在绝对必要时才复制,避免不必要的复制
{
--this->uP->u;
this->uP = new UPoint(this->uP->p);
}
this->uP->p.x(x0);
return *this;
}
Handle& Handle::y(int y0)
{
if (this->uP->u != 1)
{
--this->uP->u;
this->uP = new UPoint(uP->p);
}
this->uP->p.y(y0);
return *this;
}
//如果是希望赋值以后,改变一个就影响到另外一个,
//{
// Handle h1(1, 2);
// Handle h2 = h1;
// h2.x(3);
// int n = h1.x(); //输出3
//}
//这就需要保证h1和h2绑定到同一个对象上
Handle& Handle::x(int x0)
{
uP->p.x(x0);
return *this;
}
Handle& Handle::y(int y0)
{
uP->p.y(y0);
return *this;
}
int Handle::getX() const
{
return uP->p.getX();
}
int Handle::getY() const
{
return uP->p.getY();
}
(3)句柄——分离引用计数(成员)
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Point
{
public:
Point() :xVal(0), yVal(0) { }
Point(int x,int y) :xVal(x), yVal(y) { }
int getX() const
{
return xVal;
}
int getY() const
{
return yVal;
}
Point& x(int x)
{
xVal = x;
return *this;
}
Point& y(int y)
{
yVal = y;
return *this;
}
private:
int xVal;
int yVal;
};
class Handle
{
public:
Handle();
Handle(int, int);
Handle(const Point&);
Handle(const Handle&);
Handle& operator=(const Handle&);
~Handle();
private:
Point* p; //句柄类关联Point类,而不是UPoint类,这样的话我们不仅可以将Handle绑定到一个Point,还可以绑定到继承自Point类的对象上
int* u;
};
Handle::Handle() : u(new int(1)) , p(new Point)
{
}
Handle::Handle(int x, int y) : u(new int(1)), p(new Point(x, y))
{
}
Handle::Handle(const Point& p) : u(new int(1)), p(new Point(p))
{
}
Handle::Handle(const Handle& h) : u(h.u) ,p(h.p) //拷贝构造,这样原先的句柄和其副本都指向同一个UPoint对象
{
++*u; //引用计数加1,避免了复制Point
}
Handle& Handle::operator=(const Handle& h) //赋值运算符,左侧的句柄在赋值后将指向另外一个对象
{
//首先递增右侧句柄指向对象的引用计数
++*h.u;
//然后递减左侧句柄所指向对象的引用计数
if (--*u == 0)
{
delete u;
delete p;
}
//赋值
this->p = h.p;
this->u = h.u;
return *this;
}
Handle::~Handle()
{
if (--*u == 0) //递减引用计数,如果引用计数减为0,就删除Upoint对象
{
delete u;
delete p;
}
}
(4)句柄——分离引用计数(抽象成一个单独的类)
UseCount类可以在不了解其使用者任何信息的情况下与之合为一体,可以把这个类当成句柄实现的一部分,与各种不同的数据结构协同工作。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Point
{
public:
Point() :xVal(0), yVal(0) { }
Point(int x,int y) :xVal(x), yVal(y) { }
int getX() const
{
return xVal;
}
int getY() const
{
return yVal;
}
Point& x(int x)
{
xVal = x;
return *this;
}
Point& y(int y)
{
yVal = y;
return *this;
}
private:
int xVal;
int yVal;
};
class UseCount //对引用计数的抽象
{
public:
UseCount() :p(new int(1))
{
}
UseCount(const UseCount& u) : p(u.p)
{
++*p;
}
~UseCount()
{
if (--*p == 0)
delete p;
}
bool only() //描述该UseCount对象是否是唯一指向它的计数器的对象
{
return *p == 1;
}
bool reattach(const UseCount& u)
{
++*u.p;
if (--*p == 0)
{
delete p;
p = u.p;
return true;
}
p = u.p;
return false;
}
bool makeOnly()
{
if (*p == 1)
{
return false;
}
--*p;
p = new int(1);
return true;
}
private:
int *p;
};
class Handle
{
public:
Handle();
Handle(int, int);
Handle(const Point&);
Handle(const Handle&);
Handle& operator=(const Handle&);
~Handle();
int getX() const;
Handle& x(int);
int getY() const;
Handle& y(int);
private:
Point* p; //句柄类关联Point类,而不是UPoint类,这样的话我们不仅可以将Handle绑定到一个Point,还可以绑定到继承自Point类的对象上
UseCount u;
};
Handle::Handle() : p(new Point)
{
}
Handle::Handle(int x, int y) : p(new Point(x, y))
{
}
Handle::Handle(const Point& p) : p(new Point(p))
{
}
Handle::Handle(const Handle& h) : u(h.u) ,p(h.p) //拷贝构造,这样原先的句柄和其副本都指向同一个UPoint对象
{
}
Handle& Handle::operator=(const Handle& h)
{
if (u.reattach(h.u))
{
delete p;
}
this->p = h.p;
return *this;
}
Handle::~Handle()
{
if (u.only())
{
delete p;
}
}
Handle& Handle::x(int x0)
{
if (u.makeOnly())
{
p = new Point(*p);
}
p->x(x0);
return *this;
}
Handle& Handle::y(int y0)
{
if (u.makeOnly())
{
p = new Point(*p);
}
p->y(y0);
return *this;
}
int Handle::getX() const
{
return p->getX();
}
int Handle::getY() const
{
return p->getY();
}
以上是关于《C++沉思录》——类设计核查表代理类句柄类的主要内容,如果未能解决你的问题,请参考以下文章