0

我正在开发一个向用户提供统计数据的项目。我创建了一个名为 Dog 的类,它有几个功能。说话、呼呼、奔跑、取物等。

我想要一个函数来吐出每个函数被调用了多少次。我也对构造函数调用和析构函数调用感兴趣。

我有一个定义所有函数的头文件,然后是一个单独的 .cc 文件来实现它们。我的问题是,有没有办法跟踪每个函数被调用的次数?

我有一个名为 print 的函数,它将获取“统计信息”,然后将它们输出到标准输出。我正在考虑使用静态整数作为类本身的一部分,声明几个整数来跟踪这些事情。我知道编译器将创建整数的副本并将其初始化为最小值,然后我将在 .cc 函数中递增整数。

我还考虑过将静态整数作为 .cc 中的全局变量。哪种方式更容易?还是有更好的方法来做到这一点?

任何帮助是极大的赞赏!

4

5 回答 5

4

使用静态成员变量是要走的路。但是,编译器不会“创建整数的副本并将其初始化为最小值”;您必须为 .cc 文件中的每一个提供一个定义,并在那里将其初始化为 0。(如果您使用的是 C++11,情况会有所不同,但基本思想是相同的。)

没有理由使用静态全局变量而不是静态成员。

富.h:

class Foo {
  static int countCtor_;
  static int countDtor_;
  static int countprint_:
  Foo();
  ~Foo();
  static void print();
};

foo.cc:

#include <iostream>
#include "foo.h"

int Foo::countCtor_ = 0;
int Foo::countDtor_ = 0;
int Foo::countprint_ = 0;

Foo::Foo() {
  ++countCtor_;
  // Something here
}
Foo::~Foo() {
  ++countDtor_;
  // Something here
}
void Foo::print() {
  ++countprint_;
  std::cout << "Ctor:  " << countCtor_ << "\n"
            << "Dtor:  " << countDtor_ << "\n"
            << "print: " << countprint_ << "\n";
}

但是,如果你有很多函数,所涉及的重复有点烦人——当你的意思是 ++countBaz_ 时,很容易意外地执行 ++countBar_ (特别是如果你复制和粘贴样板文件),所以你可能想要一些东西有点花哨,例如静态映射和递增计数[__FUNC__]的宏,因此您可以在每个函数中使用完全相同的行。像这样:

富.h:

#include <map>
class Foo {
  static std::map<const char*, int> counts_;
  Foo();
  ~Foo();
  void print();
};

foo.cc:

#include <iostream>
#include "foo.h"

std::map<const char *, int> Foo::counts_;

#define INC_COUNT_() do { ++counts_[__FUNC__]; } while (0)

Foo::Foo() {
  INC_COUNT_();
  // Something here
}
Foo::~Foo() {
  INC_COUNT_();
  // Something here
}
void Foo::print() {
  INC_COUNT_();
  for (std::map<const char *, int>::const_iterator it = counts_.begin(); 
       it != counts_.end(); ++it) {
    std::cout << it->first << ": " << it->second << "\n";
  }
}

在上面的示例代码中,__FUNC__ 是一个占位符。不幸的是,没有可以使用的符合标准的值来代替它。大多数编译器都有一些 __func__、__FUNC__、__FUNCTION__、__FUNCSIG__ 和 __PRETTY_FUNCTION__ 的子集。但是,这些都不是 C++03 中的标准。C++11 确实将 __func__ 标准化,但只是作为“实现定义的字符串”,不能保证它是有用的,甚至是唯一的。最重要的是,不同编译器的值会有所不同。此外,其中一些可能是宏而不是标识符,以使事情变得更有趣。

如果你想要真正可移植的代码,在 C++11 中,你可以使用类似 string(__func__) + ":" + STRINGIZE(__LINE__) 的东西——这有点难看,但至少每个函数都有一个唯一的名称。而在 C++03 中,没有等价物。如果您只需要“足够便携”,请查阅您使用的每个编译器的文档,或者依赖 autoconf 之类的东西。

于 2012-07-02T21:21:46.943 回答
3

您是否有任何理由不能使用标准分析工具来为您计算这些调用?像gprof的东西?

否则静态整数将是要走的路。

于 2012-07-02T21:18:14.713 回答
2

Assuming you want these statistics tracked all the time in your program, you could use an unordered_map of your function names:

std::unordered_map<const char *, unsigned> stats;

void foo () {
    // use __FUNCDNAME__ for MSVC
    ++stats[__PRETTY_FUNCTION__];
    //...
}

The use of compiler specific function name specifiers is purposefully there to get the decorated function names. This is so that overloaded function names get counted as separate functions.

This technique allows you to add new functions easily without thinking about anything else, but there is a small additional cost if there are hash collisions (which can be remedied somewhat by sizing the stats map to be larger). There is no hash computed on the string, since the key is a pointer type, it just uses the pointer value itself as the hash.

If this is just one-off code for profiling, then you should first try to use the code profiling tools available on your platform.

于 2012-07-02T21:30:07.020 回答
1

您可以将static本地变量放在方法本身中,这看起来更简洁,因为这些变量在逻辑上没有连接到类,因此没有理由让它们成为成员。

另外,你可以有一个宏来简化工作。我通常不建议使用宏,但这似乎是一个合适的用途:

#define DEFINE_COUNTER \
   static int noCalls = 0; \
   noCalls++;


void foo()
{
   DEFINE_COUNTER
}
于 2012-07-02T21:16:43.663 回答
1

使用实现观察者模式或方法调用拦截的库。你可以从这个列表中选择一个,或者使用维生素之类的东西。

于 2012-07-02T21:21:41.157 回答