c++ - 避免 RAII 计时器对象中的虚假构造和破坏

标签 c++ performance c++11 factory

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

相关文章:

C++:部分特化的手动消歧(使用 SFINAE)

c++ - 使用带有 STL 容器的 boost.serialization 作为模板参数

c++ - 我应该如何将数据类链接到我的 GUI 代码(以在 C++ 中显示对象的属性)?

c++ - std::begin 可以使用数组参数吗?如果可以,如何?

javascript - 将较小的对象传递给函数是否可以更好地提高性能

Clojure HashMap 查找的性能问题

java - 如何为 Android ndk 准备 .cpp 文件来构建它们?

jquery - 使用 window.resize (或其他方法)触发 jquery 函数并使 gridster.js 响应的有效方法

c++ - 不完整类型的无效使用(类方法特化)

c++ - 是否允许在 constexpr 函数中进行函数指针比较?