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