簡體   English   中英

避免RAII計時器對象中的虛假構造和破壞

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM