[英]Avoiding spurious construction and destruction in RAII timer object
我有一個Timer
對象,它應該將代碼區域從其構造計時到其銷毀。 這些Timer
對象由長期存在的TimerManager
對象創建並與之關聯。 實際上, Timer
對象只是指向TimerManager
的指針的薄包裝器,它可以完成繁重的操作。
我希望用戶像這樣的Timer
對象:
TimerManager manager; // maybe a global, thread local, whatever
...
{
Timer timer = manager.newTimer();
// code under test
} // timer object destroyed and timer stops here
Timer
對象可以像下面這樣簡單:
class Timer {
TimerManager* manager_;
public:
Timer(TimerManager* manager) : manager_(manager) {
manager_->start();
}
~Timer() {
manager_->stop();
}
};
在這里,啟動和停止計時器的所有繁重工作都委托給經理。
但是,如果我像這樣實現TimerManager::newTimer()
:
TimerManager::newTimer() {
Timer t(this);
// ...
return t;
}
然后根據RVO是否啟動,我可能會得到一個假的構造和破壞Timer
對象t
,這與我想在調用代碼中計算的實際區域不同。
我可以使用以下代碼來初始化Timer對象:
{
Timer timer(&manager);
// code under test
} // timer object destroyed and timer stops here
這確保不會創建或銷毀額外的Timer
對象,但我更喜歡賦值語法,特別是因為它允許我使用具有不同行為的各種newTimer()
類型方法。 有沒有辦法得到這樣的東西沒有Timer
創建和破壞的額外副作用。
表現很重要。
我沒有使用C ++ 17所以我無法利用保證返回值優化。
您需要使管理器不可復制並提供適當的移動操作。 移動操作應該傳輸資源並將移動的管理器設置為nullptr
。 析構函數應該能夠處理nullptr
情況。 如:
class Timer {
TimerManager* manager_;
public:
Timer(TimerManager* manager) : manager_(manager) {
manager_->start();
}
Timer(const Timer&) = delete; // noncopyable
Timer(Timer&& timer) // move constructor
:manager_{nullptr}
{
swap(*this, timer);
}
Timer& operator=(Timer timer) // (move-only) assignment operator
{
swap(*this, timer);
return *this;
}
friend void swap(Timer& lhs, Timer& rhs)
{
swap(lhs.manager_, rhs.manager_);
}
~Timer() { // take care of nullptr
if (manager_)
manager_->stop();
}
};
我在這里使用了復制和交換習語。 這樣,如果返回Timer
,就像在
TimerManager::newTimer() {
Timer t(this);
// ...
return t;
}
然后移動t
而不是復制。 只傳遞指針,計時器不會中斷。 並且計時器僅啟動和停止一次。
此外,如果您有效地使用庫,即使用自定義刪除器的unique_ptr
,則整個過程是不必要的:
struct Stopper {
void operator()(TimerManager* tm)
{
tm->stop();
}
};
class Timer {
std::unique_ptr<TimerManager, Stopper> manager_;
public:
Timer(TimerManager* manager)
:manager_{manager}
{
manager_->start();
}
// everything is automatically correct
};
如果您可以將您的功能重新組織為:
Timer TimerManager::newTimer() {
// ...
t(this);
return t; // Depend of NRVO :(
}
那么你可以使用copy-list-initialization來構造返回的對象:
Timer TimerManager::newTimer() {
// ...
return {this}; // construct returned object in place, as NRVO :-)
}
但你仍然有可能采取行動:
Timer timer = manager.newTimer();
您可以將其更改為:
const Timer& timer = manager.newTimer();
要么
Timer&& timer = manager.newTimer();
刪除副本和移動構造函數使該構造可行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.