《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的成員函數”。
代理類的作用:允許在一個容器中儲存類型不同但是相互關聯的對象。它允許將整個派生層次壓縮在一個對象類型中。代理類是句柄類中最簡單的一種。
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);
(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(); }