【问题标题】:Static vs. member variable静态与成员变量
【发布时间】:2009-03-09 09:14:37
【问题描述】:

为了调试,我想在我的类中添加一些计数器变量。但是最好不要更改标头导致大量重新编译。

如果我正确理解了关键字,那么以下两个 sn-ps 将完全相同。当然假设只有一个实例。

class FooA
{
public:
    FooA() : count(0) {}
    ~FooA() {}

    void update()
    {
        ++count;
    }

private:
    int count;
};

对比

class FooB
{
public:
    FooB() {}
    ~FooB() {}

    void update()
    {
        static int count = 0;
        ++count;
    }
};

FooA 中,可以在类中的任何位置访问 count,并且还会使标题膨胀,因为变量 应该 在不再需要时被删除。

FooB 中,该变量仅在它所在的一个函数中可见。以后容易删除。我能想到的唯一缺点是 FooB 的计数在类的所有实例之间共享,但这对我来说不是问题。

  • 是否正确使用了关键字?我假设一旦在 FooB 中创建了 count,它就会保持创建状态,并且不会在每次调用更新时重新初始化为零。
  • 是否还有其他需要注意的警告或提示?

编辑:在被告知这会在多线程环境中导致问题后,我澄清说我的代码库是单线程的。

【问题讨论】:

    标签: c++ debugging static variables


    【解决方案1】:

    您对静态函数变量的假设是正确的。如果您从多个线程访问它,它可能不正确。考虑使用 InterlockedIncrement()。

    【讨论】:

      【解决方案2】:

      对于您的长期 C++ 工具箱,您真正想要的是一个线程安全的通用调试计数器类,它允许您将它放在任何地方并使用它,并且可以从其他任何地方访问以打印它。如果您的代码对性能敏感,您可能希望它在非调试版本中自动不执行任何操作。

      此类类的接口可能如下所示:

      class Counters {
      public:
        // Counters singleton request pattern.
        // Counters::get()["my-counter"]++;
        static Counters& get() {
          if (!_counters) _counters = new Counters();
        }
      
        // Bad idea if you want to deal with multithreaded things.
        // If you do, either provide an Increment(int inc_by); function instead of this,
        // or return some sort of atomic counter instead of an int.
        int& operator[](const string& key) {
          if (__DEBUG__) {
            return _counter_map.operator[](key);
          } else {
            return _bogus;
          }
        }
      
        // you have to deal with exposing iteration support.
      
      private:
        Counters() {}
      
        // Kill copy and operator=
        void Counters(const Counters&);
        Counters& operator=(const Counters&);
      
        // Singleton member.
        static Counters* _counters;
      
        // Map to store the counters.
        std::map<string, int> _counter_map;
      
        // Bogus counter for opt builds.
        int _bogus;
      };
      

      一旦你有了这个,你可以通过调用将它随意放在你想要的 .cpp 文件中的任何地方:

      void Foo::update() {
        // Leave this in permanently, it will automatically get killed in OPT.
        Counters::get()["update-counter"]++;
      }
      

      如果你有内置的迭代支持,你可以这样做:

      int main(...) {
        ...
        for (Counters::const_iterator i = Counters::get().begin(); i != Countes::get().end(); ++i) {
          cout << i.first << ": " << i.second;
        }
        ...
      }
      

      创建 counters 类有些繁重,但如果您正在编写一堆 cpp 编码,您可能会发现编写一次然后能够将其作为任何 lib 的一部分链接到其中很有用。

      【讨论】:

        【解决方案3】:

        静态变量与多线程一起使用时会出现主要问题。如果你的应用是单线程的,那么你的做法是完全正确的。

        【讨论】:

          【解决方案4】:

          在这种情况下,我通常会在类的源文件中将 count 放入匿名命名空间中。这意味着您可以随意添加/删除变量,它可以在文件中的任何位置使用,并且不会发生名称冲突。它确实有一个缺点,它只能在源文件中的函数中使用,而不是在头文件中的内联函数中使用,但我认为这就是你想要的。

          在文件 FooC.cpp 中

          namespace {
          int count=0;
          }
          
          void FooC::update()
          {
              ++count;
          }
          

          【讨论】:

          • 为什么要将它添加到命名空间?你真的可以随意添加/删除它吗?如果我在那里删除命名空间定义,那么 ++count 不会;行导致编译错误?
          猜你喜欢
          • 2016-05-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多