我正在开发一个向用户提供统计信息的项目。我创建了一个名为Dog的类, 它有几个功能。说话,说话,跑步,取票等。
我希望有一个函数可以调出每个函数的调用次数。我也对构造函数调用和析构函数调用感兴趣。
我有一个定义所有函数的头文件,然后是一个实现它们的单独的.cc文件。我的问题是,有没有办法跟踪每个函数的调用次数?
我有一个名为print的函数,它将获取“统计信息”,然后将它们输出到标准输出。我正在考虑使用静态整数作为类本身的一部分,声明几个整数来跟踪这些事情。我知道编译器会创建一个整数的副本并将其初始化为最小值,然后我将增加.cc函数中的整数。
我还考虑过将静态整数作为.cc中的全局变量。哪种方式更容易?或者有更好的方法吗?
非常感谢任何帮助!
答案 0 :(得分:4)
使用静态成员变量是可行的方法。但是,编译器不会“创建整数的副本并将其初始化为最小值”;你必须为.cc文件中的每一个提供一个定义,并在那里将其初始化为0。 (如果您使用的是C ++ 11,情况会有所不同,但基本思路是一样的。)
没有理由使用静态全局变量而不是静态成员。
foo.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__]的宏,所以你可以在每个函数中使用完全相同的行。像这样:
foo.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中,你可以使用像字符串(__ func__)+“:”+ STRINGIZE(__ LINE __)这样的东西 - 这有点难看,但至少每个函数都有一个独特的名称。在C ++ 03中,没有相应的东西。如果你只需要“足够便携”,请查阅你使用的每个编译器的文档,或者依赖autoconf这样的东西。
答案 1 :(得分:3)
您是否有任何理由不能使用标准的分析工具来统计这些来电?像gprof这样的东西?
否则静态整数将成为可能。
答案 2 :(得分:2)
假设您希望在程序中始终跟踪这些统计信息,您可以使用unordered_map
函数名称:
std::unordered_map<const char *, unsigned> stats;
void foo () {
// use __FUNCDNAME__ for MSVC
++stats[__PRETTY_FUNCTION__];
//...
}
有目的地使用编译器特定的函数名称说明符来获取修饰的函数名称。这样可以将重载的函数名称计为单独的函数。
这种技术允许您轻松添加新功能而无需考虑其他任何事情,但如果存在哈希冲突(通过将stats
地图的大小调整为更大可以稍微补救一下,则会有一些额外的成本)。在字符串上没有计算哈希值,因为键是指针类型,它只使用指针值本身作为哈希值。
如果这只是用于分析的一次性代码,那么您应该首先尝试使用平台上提供的代码分析工具。
答案 3 :(得分:1)
你可以将static
本地人放在方法本身内,这似乎更清晰,因为这些变量在逻辑上与班级无关,所以没有理由让他们成员。
另外,您可以使用宏来简化工作。我通常不建议使用宏,但这似乎是一个合适的用途:
#define DEFINE_COUNTER \
static int noCalls = 0; \
noCalls++;
void foo()
{
DEFINE_COUNTER
}
答案 4 :(得分:1)