程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 《C++沉思錄》——類設計核查表、代理類、句柄類

《C++沉思錄》——類設計核查表、代理類、句柄類

編輯:關於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的成員函數”。

 

二、代理類(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 
#include 
#include 
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 
#include 
#include 
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 
#include 
#include 
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();
}
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved