網站經營與建設優(yōu)化大師軟件下載
目錄
一、unique_lock
二、智能指針? (其實是一個類)
三、工廠模式
一、unique_lock
參考文章【1】,了解unique_lock與lock_guard的區(qū)別。
總結:unique_lock使用起來要比lock_guard更靈活,但是效率會第一點,內存的占用也會大一點。
1.unique_lock的定義:
std::mutex mlock;
std::unique_lock<std::mutex> munique(mlock);
第二個參數可以是std::adopt_lock
std::unique_lock<std::mutex> munique(mlock,std::adopt_lock);
使用unique_lock和mutex來實現互斥鎖。unique_lock在構造函數中獲取鎖,析構函數中釋放鎖。adopt_lock參數告訴unique_lock構造函數,鎖已經被其他線程獲取,unique_lock不需要再次獲取鎖,而是直接將鎖的所有權轉移給它自己。這樣做的好處是可以避免死鎖,因為uniquelock的析構函數會在任何情況下都釋放鎖,即使在發(fā)生異常的情況下也是如此。
也可以是:std::try_to_lock
std::unique_lock<std::mutex> munique(mlock, std::try_to_lock);
如果有一個線程被鎖住,而且執(zhí)行時間很長,那么另一個線程一般會被阻塞在那里,反而會造成時間的浪費。那么使用了try_to_lock后,如果被鎖住了,它不會在那里阻塞等待,它可以先去執(zhí)行其他沒有被鎖的代碼。
也可以是std::defer_lock
std::unique_lock<std::mutex> munique(mlock, std::defer_lock);
表示暫時先不lock,之后手動去lock。一般與unique_lock的成員函數搭配使用
2.unique_lock的成員函數
lock() 與 unlock()
當使用了defer_lock參數時,在創(chuàng)建了unique_lock的對象時就不會自動加鎖,那么就需要借助lock這個成員函數來進行手動加鎖,當然也有unlock來手動解鎖。
try_lock():判斷是否能拿到鎖,如果拿不到鎖,返回false,如果拿到了鎖,返回true
release():解除unique_lock和mutex對象的聯(lián)系,并將原mutex對象的指針返回出來。如果之前的mutex已經加鎖,需在后面自己手動unlock解鎖
std::unique_lock<std::mutex> munique(mlock); // 這里是自動lock
std::mutex *m = munique.release();
....
m->unlock();
std::thread t1(work1, std::ref(ans));
std::ref是C++標準庫中的一個函數模板,用于將一個對象包裝成一個引用。這個函數模板的作用是將一個對象的引用傳遞給一個函數,而不是將對象本身傳遞給函數。這樣做的好處是可以避免對象的拷貝,提高程序的效率。
代碼塊中,std::ref被用于將ans對象包裝成一個引用,并將這個引用傳遞給work1函數的線程。這樣做的目的是讓work1函數在線程中對ans對象進行操作,而不是對ans對象的拷貝進行操作。
二、智能指針? (其實是一個類)
智能指針主要用于管理在堆上分配的內存,它將普通的指針封裝為一個棧對象。當棧對象的生存周期結束后,會在析構函數中釋放掉申請的內存,從而防止內存泄漏。
C++ 11中最常用的智能指針類型為shared_ptr,它采用引用計數的方法,記錄當前內存資源被多少個智能指針引用。該引用計數的內存在堆上分配。當新增一個時引用計數加1,當過期時引用計數減一。只有引用計數為0時,智能指針才會自動釋放引用的內存資源。對shared_ptr進行初始化時不能將一個普通指針直接賦值給智能指針,因為一個是指針,一個是類??梢酝ㄟ^make_shared函數或者通過構造函數傳入普通指針。并可以通過get函數獲得普通指針。
1.unique_ptr
unique_ptr保證同一時間內只有一個智能指針可以指向該對象。它對于避免資源泄露,以new創(chuàng)建對象后因為發(fā)生異常而忘記調用delete特別有用。
兩個unique_ptr 不能指向一個對象,即 unique_ptr 不共享它所管理的對象。它無法復制到其他 unique_ptr,無法通過值傳遞到函數。
#include <iostream>
#include <string>
#include <memory>
int main() {std::unique_ptr<std::string> ps1, ps2;ps1 = std::make_unique<std::string>("hello");ps2 = std::move(ps1);ps1 = std::make_unique<std::string>("alexia");std::cout << *ps2 << *ps1 << std::endl;return 0;
}
2.shared_ptr
shared_ptr 允許多個指針指向同一個對象。利用引用計數的方式實現了對所管理的對象的所有權的分享,即允許多個 shared_ptr 共同管理同一個對象,當引用計數為 0 的時候,自動釋放資源。
成員函數:
use_count 返回引用計數的個數
unique 返回是否是獨占所有權
swap 交換兩個 shared_ptr 對象(即交換所擁有的對象)
reset 放棄內部對象的所有權或擁有對象的變更, 會引起原有對象的引用計數的減少
get 返回內部對象(指針), 由于已經重載了()方法, 因此和直接使用對象是一樣的.如
shared_ptr<int> sp(new int(1));
sp 與 sp.get()是等價的。
3.weak_ptr
share_ptr雖然已經很好用了,但是有一點share_ptr智能指針還是有內存泄露的情況,當兩個對象相互使用一個shared_ptr成員變量指向對方,會造成循環(huán)引用,使引用計數失效,從而導致內存泄漏。
weak_ptr 被設計為與 shared_ptr 共同工作,可以從一個 shared_ptr 或者另一個 weak_ptr 對象構造而來。weak_ptr 是為了配合 shared_ptr 而引入的一種智能指針,它更像是 shared_ptr 的一個助手而不是智能指針,因為它不具有普通指針的行為,沒有重載 operator* 和 operator-> ,因此取名為 weak,表明其是功能較弱的智能指針。它的最大作用在于協(xié)助 shared_ptr 工作,可獲得資源的觀測權,像旁觀者那樣觀測資源的使用情況。觀察者意味著 weak_ptr 只對 shared_ptr 進行引用,而不改變其引用計數,當被觀察的 shared_ptr 失效后,相應的 weak_ptr 也相應失效。
使用方法:
使用 weak_ptr 的成員函數 use_count() 可以觀測資源的引用計數。注意:weak_ptr不增加引用計數
另一個成員函數 expired() 的功能等價于 use_count()==0,表示被觀測的資源(也就是 shared_ptr 管理的資源)已經不復存在。
weak_ptr 可以使用一個非常重要的成員函數lock(),從被觀測的 shared_ptr 獲得一個可用的 shared_ptr 管理的對象, 從而操作資源。但當 expired()==true 的時候,lock() 函數將返回一個存儲空指針的 shared_ptr??偟膩碚f,weak_ptr 的基本用法總結如下:
shared_ptr<int> sp(new int(1));
weak_ptr<T> w; //創(chuàng)建空 weak_ptr,可以指向類型為 T 的對象
weak_ptr<T> w(sp); //與 shared_ptr 指向相同的對象,shared_ptr 引用計數不變。T必須能轉換為 sp 指向的類型
w=p; //p 可以是 shared_ptr 或 weak_ptr,賦值后 w 與 p 共享對象
w.reset(); //將 w 置空
w.use_count(); //返回與 w 共享對象的 shared_ptr 的數量
w.expired(); //若 w.use_count() 為 0,返回 true,否則返回 false
w.lock(); //如果 expired() 為 true,返回一個空 shared_ptr,否則返回非空 shared_ptr
#include <assert.h>#include <iostream>
#include <memory>
#include <string>using namespace std;int main() {shared_ptr<int> sp(new int(10));assert(sp.use_count() == 1);weak_ptr<int> wp(sp); // 從 shared_ptr 創(chuàng)建 weak_ptrassert(wp.use_count() == 1);if (!wp.expired()) { // 判斷 weak_ptr 觀察的對象是否失效shared_ptr<int> sp2 = wp.lock(); //使用 wp.lock() 創(chuàng)建一個新的 shared_ptr 時,它又增加了一次引用計數 *sp2 = 100;assert(wp.use_count() == 2);}assert(wp.use_count() == 1); //在 if 語句塊之外,wp.use_count() 的值仍然是1,因為 weak_ptr 并不會增加引用計數。cout << "int:" << *sp << endl;return 0;
}
這段代碼主要演示了如何使用?weak_ptr?來避免循環(huán)引用的問題。在這個例子中,我們創(chuàng)建了一個?shared_ptr?對象?sp,然后通過?weak_ptr?對象?wp?來觀察?sp。如果?sp?被銷毀了,那么?wp?也會自動失效。在代碼中,我們通過?wp.expired()?來判斷?wp?是否失效,如果沒有失效,我們就可以通過?wp.lock()?來獲得一個?shared_ptr?對象?sp2,然后修改?sp2?所指向的值。最后,我們通過?use_count()?來檢查?sp?和?wp?的引用計數是否正確。
4.如何選擇智能指針:
(1)如果程序要使用多個指向同一個對象的指針,應選擇 shared_ptr
- 將指針作為參數或者函數的返回值進行傳遞的話,應該使用 shared_ptr;
- 兩個對象都包含指向第三個對象的指針,此時應該使用 shared_ptr 來管理第三個對象;
- STL 容器包含指針。很多 STL 算法都支持復制和賦值操作,這些操作可用于 shared_ptr,但不能用于 unique_ptr(編譯器發(fā)出 warning)和 auto_ptr(行為不確定)。如果你的編譯器沒有提供 shared_ptr,可使用 Boost 庫提供的 shared_ptr。
(2)如果程序不需要多個指向同一個對象的指針,則可使用 unique_ptr。如果函數使用 new 分配內存,并返還指向該內存的指針,將其返回類型聲明為 unique_ptr 是不錯的選擇。
(3)為了解決 shared_ptr 的循環(huán)引用問題,使用?weak_ptr。
參考【2】【3】
三、工廠模式
工廠模式分為簡單工廠模式、工廠方法模式和抽象工廠模式。
1.簡單工廠模式
簡單工廠模式又叫靜態(tài)方法模式(因為工廠類定義了一個靜態(tài)方法)。
將“類實例化的操作”與“使用對象的操作”分開,讓使用者不用知道具體參數就可以實例化出所需要的“產品”類,從而避免了在客戶端代碼中顯式指定,實現了解耦。也就是說,使用者可直接消費產品而不需要知道其生產的細節(jié)。
模式組成:
組成 | 關系? | 作用 |
抽象產品(Product) | 具體產品的父類 | 描述產品的公共接口 |
具體產品(Concrete Product) | 抽象產品的子類;工廠類創(chuàng)建的目標類 | 描述生產的具體產品 |
工廠(Factor) | 被外界調用 | 根據傳入不同參數從而創(chuàng)建不同具體產品類的實例 |
使用步驟:
- 創(chuàng)建抽象產品類Product? ?:定義產品的公共接口
- 創(chuàng)建具體產品類(繼承抽象產品類):定義生產的具體產品
- 創(chuàng)建工廠類:通過創(chuàng)建靜態(tài)方法傳入不同參數,從而創(chuàng)建不同具體產品類的實例
- 外界通過調用工廠類的靜態(tài)方法,傳入不同參數從而創(chuàng)建不同具體產品類的實例
實列:某加工廠推出三個產品,使用簡單工廠模式實現三種產品的生成
①創(chuàng)建抽象產品類Product:
class Product
{
public:virtual void Show() = 0;
};
②創(chuàng)建具體產品類(繼承抽象產品類):
class ProductA : public Product
{
public:void Show(){cout<<"I'm ProductA"<<endl;}
};class ProductB : public Product
{
public:void Show(){cout<<"I'm ProductB"<<endl;}
};class ProductC : public Product
{
public:void Show(){cout<<"I'm ProductC"<<endl;}
};
③創(chuàng)建工廠類:通過創(chuàng)建靜態(tài)方法傳入不同參數,從而創(chuàng)建不同具體產品類的實例。
typedef enum ProductTypeTag
{TypeA,TypeB,TypeC
}PRODUCTTYPE;class Factory
{
public:static Product* CreateProduct(PRODUCTTYPE type){switch (type){case TypeA:return new ProductA();case TypeB:return new ProductB();case TypeC:return new ProductC();default:return NULL;}}
};
④外界通過調用工廠類的靜態(tài)方法,傳入不同參數從而創(chuàng)建不同具體產品類的實例
int main(int argc, char *argv[])
{//創(chuàng)造工廠對象Factory *ProductFactory = new Factory();//從工廠對象創(chuàng)造產品A對象Product *productObjA = ProductFactory->CreateProduct(TypeA);if (productObjA != NULL)productObjA->Show();Product *productObjB = ProductFactory->CreateProduct(TypeB);if (productObjB != NULL)productObjB->Show();Product *productObjC = ProductFactory->CreateProduct(TypeC);if (productObjC != NULL)productObjC->Show();delete ProductFactory;ProductFactory = NULL;delete productObjA;productObjA = NULL;delete productObjB;productObjB = NULL; delete productObjC;productObjC = NULL;return 0;
}
?優(yōu)缺點:
優(yōu)點:
- 把初始化實例時的工作放到工廠里進行,使代碼更容易維護。
- 將“類實例化的操作”與“使用對象的操作”分開,讓使用者不用知道具體參數就可以實例化出所需要的“產品”類,
缺點:
- 工廠類集中了所有實例(產品)的創(chuàng)建邏輯,一旦這個工廠不能正常工作,整個系統(tǒng)都會受到影響;
- 違背“開放 - 關閉原則”,一旦添加新產品就不得不修改工廠類的邏輯,這樣就會造成工廠邏輯過于復雜。
- 簡單工廠模式由于使用了靜態(tài)工廠方法,靜態(tài)方法不能被繼承和重寫,會造成工廠角色無法形成基于繼承的等級結構。
2.工廠方法模式
針對簡答工廠模式問題,設計了工廠方法模式。工廠父類負責定義創(chuàng)建對象的公共接口,而子類則負責生成具體的對象。
將類的實例化(具體產品的創(chuàng)建)延遲到工廠類的子類(具體工廠)中完成,即由子類來決定應該實例化(創(chuàng)建)哪一個類。
之所以可以解決簡單工廠的問題,是因為工廠方法模式把具體產品的創(chuàng)建推遲到工廠類的子類(具體工廠)中,此時工廠類不再負責所有產品的創(chuàng)建,而只是給出具體工廠必須實現的接口,這樣工廠方法模式在添加新產品的時候就不修改工廠類邏輯而是添加新的工廠子類,符合開放封閉原則。
模式組成:
組成 | 關系? | 作用 |
抽象產品(Product) | 具體產品的父類 | 描述具體產品的公共接口 |
具體產品(Concrete Product) | 抽象產品的子類;工廠類創(chuàng)建的目標類 | 描述生產的具體產品 |
抽象工廠(Factor) | 具體工廠的父類 | 描述具體工廠的公共接口 |
具體工廠(Concrete Factor) | 抽象工廠的子類;被外界調用 | 描述具體工廠 |
使用步驟:
- 創(chuàng)建抽象產品類 ,定義具體產品的公共接口;
- 創(chuàng)建具體產品類(繼承抽象產品類):定義生產的具體產品;
- 創(chuàng)建抽象工廠類,定義具體工廠的公共接口;
- 創(chuàng)建具體工廠類(繼承抽象工廠類),定義創(chuàng)建對應具體產品實例的方法;
- 外界通過調用具體工廠類的方法,從而創(chuàng)建不同具體產品類的實例
實例:某加工廠原來只生產A類產品,但新的訂單要求生產B類產品,由于改變原來工廠的配置比較困難,因此開設工廠B生產B類產品。
#include <iostream>
using namespace std;//1.創(chuàng)建抽象產品類
class Product
{
public:virtual void Show() = 0;
};
//2.創(chuàng)建具體產品類(繼承抽象產品類)
class ProductA : public Product
{
public:void Show(){cout<< "I'm ProductA"<<endl;}
};class ProductB : public Product
{
public:void Show(){cout<< "I'm ProductB"<<endl;}
};//3.創(chuàng)建抽象工廠類
class Factory
{
public:virtual Product *CreateProduct() = 0;
};//4.創(chuàng)建具體工廠類(繼承抽象工廠類),定義創(chuàng)建對應具體產品實例的方法;
class FactoryA : public Factory
{
public:Product *CreateProduct(){return new ProductA ();}
};class FactoryB : public Factory
{
public:Product *CreateProduct(){return new ProductB ();}
};//5.外界通過調用具體工廠類的方法,從而創(chuàng)建不同具體產品類的實例
int main(int argc , char *argv [])
{Factory *factoryA = new FactoryA ();Product *productA = factoryA->CreateProduct();productA->Show();Factory *factoryB = new FactoryB ();Product *productB = factoryB->CreateProduct();productB->Show();if (factoryA != NULL){delete factoryA;factoryA = NULL;}if (productA != NULL){delete productA;productA = NULL;}if (factoryB != NULL){delete factoryB;factoryB = NULL;}if (productB != NULL){delete productB;productB = NULL;}system("pause");return 0;
}
優(yōu)缺點:
優(yōu)點:
- 符合開-閉原則。新增一種產品時,只需要增加相應的具體產品類和相應的工廠子類即可。(簡單工廠模式需要修改工廠類的判斷邏輯)
- 符合單一職責原則。每個具體工廠類只負責創(chuàng)建對應的產品。(簡單工廠中的工廠類存在復雜的switch邏輯判斷)
- 不使用靜態(tài)工廠方法,可以形成基于繼承的等級結構。(簡單工廠模式的工廠類使用靜態(tài)工廠方法)
缺點:
- 添加新產品時,除了增加新產品類外,還要提供與之對應的具體工廠類,系統(tǒng)類的個數將成對增加,在一定程度上增加了系統(tǒng)的復雜度
- 由于考慮到系統(tǒng)的可擴展性,需要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增加了系統(tǒng)的抽象性和理解難度
- 一個具體工廠只能創(chuàng)建一種具體產品
3.抽象工廠模式
工廠方法模式存在一個嚴重的問題:一個具體工廠只能創(chuàng)建一類產品;而在實際過程中,一個工廠往往需要生產多類產品。使用了一種新的設計模式:抽象工廠模式。
定義:創(chuàng)建一系列相關或相互依賴對象的接口,而無須指定它們具體的類;具體的工廠負責實現具體的產品實例。
允許使用抽象的接口來創(chuàng)建一組相關產品,而不需要知道或關心實際生產出的具體產品是什么,這樣就可以從具體產品中被解耦。
模式組成:
組成 | 關系? | 作用 |
抽象產品族(AbstractProduct | 抽象產品的父類 | 描述抽象產品的公共接口 |
抽象產品(Product) | 具體產品的父類 | 描述具體產品的公共接口 |
具體產品(Concrete Product) | 抽象產品的子類;工廠類創(chuàng)建的目標類 | 描述生產的具體產品 |
抽象工廠(Factor) | 具體工廠的父類 | 描述具體工廠的公共接口 |
具體工廠(Concrete Factor) | 抽象工廠的子類;被外界調用 | 描述具體工廠 |
使用步驟:
- 創(chuàng)建抽象產品族類 ,定義抽象產品的公共接口;
- 創(chuàng)建抽象產品類 (繼承抽象產品族類),定義具體產品的公共接口;
- 創(chuàng)建具體產品類(繼承抽象產品類) & 定義生產的具體產品;
- 創(chuàng)建抽象工廠類,定義具體工廠的公共接口;
- 創(chuàng)建具體工廠類(繼承抽象工廠類),定義創(chuàng)建對應具體產品實例的方法;
- 客戶端通過實例化具體的工廠類,并調用其創(chuàng)建不同目標產品的方法 創(chuàng)建不同具體產品類的實例
實例:某廠有兩個加工廠A和B分別生產鞋和衣服,隨著訂單的增加,A廠所在地有了衣服的訂單,B廠所在地有了鞋子的訂單,再開新的工廠顯然是不現實的,因此在原來的工廠增加生產需求的功能,即A生產鞋+衣服,B生產衣服+鞋。
#include <iostream>
using namespace std;
//這個代碼沒有抽象產品族類// 抽象產品A
class ProductA
{
public:virtual void Show() = 0;
};//具體的產品
class ProductA1 : public ProductA
{
public:void Show(){cout<<"I'm ProductA1"<<endl;}
};class ProductA2 : public ProductA
{
public:void Show(){cout<<"I'm ProductA2"<<endl;}
};// 抽象產品B
class ProductB
{
public:virtual void Show() = 0;
};class ProductB1 : public ProductB
{
public:void Show(){cout<<"I'm ProductB1"<<endl;}
};class ProductB2 : public ProductB
{
public:void Show(){cout<<"I'm ProductB2"<<endl;}
};// Factory
class Factory
{
public:virtual ProductA *CreateProductA() = 0;virtual ProductB *CreateProductB() = 0;
};//具體工廠A生產A1和B1
class Factory1 : public Factory
{
public:ProductA *CreateProductA(){return new ProductA1();}ProductB *CreateProductB(){return new ProductB1();}
};class Factory2 : public Factory
{ProductA *CreateProductA(){return new ProductA2();}ProductB *CreateProductB(){return new ProductB2();}
};int main(int argc, char *argv[])
{Factory *factoryObj1 = new Factory1();ProductA *productObjA1 = factoryObj1->CreateProductA();ProductB *productObjB1 = factoryObj1->CreateProductB();productObjA1->Show();productObjB1->Show();Factory *factoryObj2 = new Factory2();ProductA *productObjA2 = factoryObj2->CreateProductA();ProductB *productObjB2 = factoryObj2->CreateProductB();productObjA2->Show();productObjB2->Show();if (factoryObj1 != NULL){delete factoryObj1;factoryObj1 = NULL;}if (productObjA1 != NULL){delete productObjA1;productObjA1= NULL;}if (productObjB1 != NULL){delete productObjB1;productObjB1 = NULL;}if (factoryObj2 != NULL){delete factoryObj2;factoryObj2 = NULL;}if (productObjA2 != NULL){delete productObjA2;productObjA2 = NULL;}if (productObjB2 != NULL){delete productObjB2;productObjB2 = NULL;}system("pause");return 0;
}
優(yōu)缺點:
優(yōu)點:
- 抽象工廠模式將具體產品的創(chuàng)建延遲到具體工廠的子類中,這樣將對象的創(chuàng)建封裝起來,可以減少客戶端與具體產品類之間的依賴,從而使系統(tǒng)耦合度低,這樣更有利于后期的維護和擴展;
- 新增一種產品類時,只需要增加相應的具體產品類和相應的工廠子類即可
缺點:
- 抽象工廠模式很難支持新種類產品的變化。因為抽象工廠接口中已經確定了可以被創(chuàng)建的產品集合,如果需要添加新產品,此時就必須去修改抽象工廠的接口,這樣就涉及到抽象工廠類的以及所有子類的改變
參考【4】 【5】?