觀察者模式(Observer),又叫發(fā)布-訂閱模式(Publish/Subscribe),定義對象間一種一對多的依賴關系,使得每當一個對象改變狀態(tài),則所有依賴于它的對象都會得到通知并自動更新。
該模式屬于行為型模式。
Subject
:抽象主題(抽象被觀察者),抽象主題角色把所有觀察者對象保存在一個集合里,每個主題都可以有任意數量的觀察者,抽象主題提供一個或者多個接口,可以增加和刪除觀察者對象。ConcreteSubject
:具體主題(具體被觀察者),該角色將有關狀態(tài)存入具體觀察者對象,在具體主題的內部狀態(tài)發(fā)生改變時,給所有注冊過的觀察者發(fā)送通知。Observer
:抽象觀察者,是觀察者者的抽象類,它定義了一個或者多個更新接口,使得在得到主題更改通知時更新自己。ConcreteObserver
:具體觀察者,實現抽象觀察者定義的更新接口,以便在得到主題更改通知時更新自身的狀態(tài)。在應用觀察者模式時需要考慮一下開發(fā)效率和運行效率的問題,程序中包括一個被觀察者、多個觀察者,將所有的觀察者都通知到會花費很多時間。如果考慮到性能問題,可以實現一個異步非阻塞的觀察者模式,在每次fire subject的時候創(chuàng)建一個新的線程執(zhí)行代碼。
觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎么發(fā)生變化的,而僅僅只是知道觀察目標發(fā)生了變化。
#include#include#include#include#includeclass Observer;
typedef std::vector>::iterator Iterator;//迭代器
//抽象被觀察者
class Subject
{public:
virtual void Attach(std::weak_ptr)=0;//注冊觀察者對象的接口
virtual Iterator Detach(Iterator it)=0;//刪除觀察者對象的接口
virtual void notifyObservers()=0;//告知所有觀察者update
};
//抽象觀察者
class Observer
{public:
virtual void update()=0;//更新自身狀態(tài)
};
//被觀察者
class ConcreteSubject :public::Subject
{public:
void Attach(std::weak_ptrs)
{//vector的add是線程安全的
observers_.push_back(s);
}
Iterator Detach(Iterator it)
{//線程不安全,要在臨界區(qū)調用此接口
return observers_.erase(it);
}
void notifyObservers()
{lock.lock();
Iterator it=observers_.begin();
while(it!=observers_.end())
{//先嘗試將weak_ptr提升為share_ptr
std::shared_ptrobj=it->lock();
if(obj)//提升成功,說明該對象未被其他線程析構
{obj->update();//更新
++it;
}
else//提升失敗,說明該對象已被其他線程析構
{//從觀察者集合中將已被析構對象刪除
//因為此處屬于臨界區(qū),是線程安全的
it=Detach(it);
}
}
lock.unlock();
}
private:
mutable std::mutex lock;
std::vector>observers_;//觀察者集合
};
//觀察者按需求定義即可,需要繼承抽象觀察者
//以下為例
class Teacher :public::Observer
{public:
void update()
{std::cout<<"Teacher is update"<std::cout<<"Teacher is ~"<public:
void update()
{std::cout<<"Student is update"<std::cout<<"Student is ~"<
測試
int main()
{ConcreteSubject subject;
std::shared_ptrteacher(new Teacher);
subject.Attach(teacher);
{std::shared_ptrstudent(new Student);
subject.Attach(student);
subject.notifyObservers();
}
subject.notifyObservers();
}
執(zhí)行結果
Teacher is update
Student is update
Student is ~
Teacher is update
Teacher is ~
迭代器失效問題分析在學習該模型時,遇到了一個bug,問題鎖定在notifyObservers()當中,當時的實現如下
void notifyObservers()
{lock.lock();
Iterator it=observers_.begin();
while(it!=observers_.end())
{std::shared_ptrobj=it->lock();
if(obj)
{obj->update();
}
else
{Detach(it);
}
it++;
}
}
當時執(zhí)行main時,就會報段錯誤,經過檢查不難發(fā)現,問題是迭代器失效了
這個問題其實很好分析,在一順序容器當中(vector,queue…等),數據是順序存儲的,當刪除一個元素后,內存中的數據會往前移動,以保證數據的緊湊。所以刪除一個元素后,該元素后面的其他元素的地址都會發(fā)生改變。在刪除操作前所持有的迭代器就會有失效的可能??聪旅娴睦?。
#include#include#includeusing namespace std;
int main()
{ vectorarr;
arr.push_back(1);
arr.push_back(2);
arr.push_back(3);
arr.push_back(4);
arr.push_back(5);
vector::iterator it=arr.begin()+3;
cout<<"刪除操作前該迭代器所指向的元素:"<
執(zhí)行結果如下
刪除操作前該迭代器所指向的元素:
4
進行刪除操作
刪除操作前該迭代器所指向的元素:
5
添加導致迭代器失效你的添加的操作也是可能導致迭代器失效的。vector的容量是動態(tài)的開辟的,當容量不夠的話,會進行realloc操作進行擴容,若該數組的后面的空間不足,realloc函數會找一片更大的空間,將原數據拷貝進去。若在此事件發(fā)生前,獲取一個迭代器,此事件發(fā)生后,該迭代器會失效。看下面例子
#include#include#includeusing namespace std;
int main()
{ vectorarr;
arr.push_back(1);
arr.push_back(2);
arr.push_back(3);
arr.push_back(4);
arr.push_back(5);
vector::iterator it=arr.begin()+3;
cout<<"此事件發(fā)生前該迭代器所指向的元素:"<arr.push_back(9);
}
cout<<"此事件發(fā)生后該迭代器所指向的元素:"<
執(zhí)行結果
此事件發(fā)生前該迭代器所指向的元素:
4
此事件發(fā)生后該迭代器所指向的元素:
21854
此時該迭代器就指向一個未初始化的空間
你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧
當前標題:設計者模式(1)觀察者模式(Observer)C++11實現-創(chuàng)新互聯(lián)
文章位置:http://muchs.cn/article26/djhccg.html
成都網站建設公司_創(chuàng)新互聯(lián),為您提供建站公司、商城網站、App設計、標簽優(yōu)化、手機網站建設、ChatGPT
聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內容