我有一个 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
};
关于c++ - 避免 RAII 计时器对象中的虚假构造和破坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57237296/