我有一个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();
  }
};

在这里,启动和停止计时器的所有繁重工作都委托(delegate)给管理器。

但是,如果我像这样实现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();
  }
};

我在这里使用了copy-and-swap习语。这样,如果返回了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
};

07-26 09:24
查看更多