在实际开发过程中,我们常常通过一个跟踪实例来跟踪我们的代码,以便于不依靠调试器就能发现代码的错误
先来看初始Trace类
class Trace { public: static bool traceIsActive; //标志跟踪是关闭还是启动 Trace(const std::string& name); ~Trace(); void debug(const std::string& msg); private: std::string theFunctionName; //存储函数的名称 }; inline Trace::Trace(const std::string& name):theFunctionName(name){ if (traceIsActive) { std::cout << "Enter function " << name << std::endl; } } inline Trace::~Trace() { if (traceIsActive) { std::cout << "Exit function " << theFunctionName << std::endl; } } inline void Trace::debug(const std::string& msg) { if (traceIsActive) { std::cout << msg << std::endl; } }
我们先看看这段代码的优点:
- 由于跟踪代码的函数会被频繁调用且代码体量小,所以我们将其内联提高性能
- 复制对象的开销是高昂的,所以我们用了引用
看上去,这段代码很完美了,但实际上这是一段十分糟糕性能极差的代码,我们看看当跟踪关闭时,这时开销最小我们的代码是怎么运作。
int myFunction(int x) { std::string name = "myFunction"; Trace t(name); t.debug("adadad"); }
我们发现即使是关闭跟踪,我们也会做至少如下事情:
- 创建string对象存储函数名
- 将函数名作为参数调用Trace构造函数
- Trace的构造函数又创建了一个成员string对象
- 销毁string变量name
- 调用Trace析构函数
- Trace销毁成员变量
有两个大问题:
- 关闭跟踪时,依旧创建没有意义的成员变量
- string对象的创建和销毁是十分消耗资源的
别小看这两个问题,它能将程序运行时间提高几十倍(从55ms到3500ms)
下面根据这两个问题进行修改:
- 用char*代替string,大大减少消耗
- 关闭跟踪时,不创建无意义的成员变量
- 将string成员变量写为string指针
class Trace { public: static bool traceIsActive; //标志跟踪是关闭还是启动 Trace(const char* name); ~Trace(); void debug(const char* msg); private: std::string* theFunctionName; //存储函数的名称 }; inline Trace::Trace(const char* name):theFunctionName(0){ if (traceIsActive) { this->theFunctionName = new std::string(name); std::cout << "Enter function " << *name << std::endl; } } inline Trace::~Trace() { if (traceIsActive) { std::cout << "Exit function " << *this->theFunctionName << std::endl; delete theFunctionName; } } inline void Trace::debug(const char* msg) { if (traceIsActive) { std::cout << *msg << std::endl; } } int myFunction(int x) { const char* name = "myFunction"; Trace t(name); t.debug("adadad"); }
这样一来相较于一开始的代码,我们的效率又有了大大的提高。