介绍:本文作为阅读书籍所做的笔记,方便于理解。知识摘取于《C++沉思录》书籍。
c++的一个基本思想就是通过类定义可以明确指明当这个类的对象被构造、销毁、复制、复制时应该发生什么事情。
这意味着设计得当的类可以为理解程序的动态行为提供一个强有力的工具。
思考问题:如何使用Trace类去阐释类机制的本质?Trace类能够以怎样的方式提供有关函数执行和类操作的调试信息?
主要思想:使程序在明显改变自身行为的同时说明自己再做什么。
这里通过两部分去分别设计Trace类来解决有关函数执行和类操作的调试信息,从而理解类机制的本质。
1.有关函数执行的调试信息
设计一个(跟踪函数)跟踪类,当我们需要知道某个函数何时被调用,就可以插入一个对trace对象的声明。
class Trace
{
public:
Trace(const char* p, const char *q) :bye(q)
{
cout << p << endl;;
}
~Trace(){ cout << bye << endl; }
private:
const char *bye;
};
void foo()
{
Trace xxx("begin foo","end foo"); //Trace对象的声明
}
使用Trace类进行调试输出过程中会产生大量的输出信息(如:begin/end),cout输出信息太多肯定会影响程序的执行效率,因此我们应该减少用户输入的文本量。考虑是禁止输出,还是重定向输出。
①方法1:将输出发送到一个特殊的“文件”中,使其实际效果相当于抛弃输出数据。(大多数操作系统)
②方法2:定义全局指针,指向调试消息输出流。(不建议,一个脆弱的易错的策略)
③方法3:创建一个channel类,管理整组Trace对象的输出。可以在程序的某一个地方针对绑定到某一个channel的所有Trace对象重定向跟踪消息的目标。
例:
class Channel;
//把实际的构造函数和析构函数“监视”起来
static const int debug = 0;
class Trace
{
public:
Trace(const char *s, Channel *c)
{
if (debug)//监视代码
{
//注意:name是显示赋值操作初始化的:name = s,
// 而不是在构造函数初始化器中初始化的:name(s)。
//保证debug已知为零时跳过初始化
name = s;
cp = c;
if (cp->trace_file)
*cp->trace_file << "begin" << name << endl;
}
}
~Trace()
{
if (debug)
{
if (cp->trace_file)
*cp->trace_file << "end" << name << endl;
}
}
private:
Channel *cp;
const char *name;
};
class Channel
{
friend class Trace;
ostream *trace_file;
public:
Channel(ostream* o = &cout) :trace_file(o){}//重定向跟踪
void reset(ostream * o){ trace_file = o; } //关闭跟踪
};
这样的监视代码可以在不必要重写代码用户代码的前提下免除几乎所有代码生成的开销。
2.生成对象的审计跟踪
设计一个(跟踪对象)跟踪类,
//每次创建一个Obj_Trace对象时,该对象都会获得一个有构造函数
//和析构函数打印的唯一序列号。
class Obj_Trace
{
public:
Obj_Trace() :ct(++count)
{
cout << "object" << ct << "constructed" << endl;
}
~Obj_Trace()
{
cout << "object" << ct << "destroyed" << endl;
}
Obj_Trace(const Obj_Trace&) :ct(++count)
{
cout << "object" << ct << "constructed" << endl;
}
Obj_Trace operator=(const Obj_Trace&)
{
return *this;
}
private:
static int count;
int ct;
};
int Obj_Trace::count = 0;
设计好Obj_Trace类,然后再来验证容器行为,来验证输出,因此设计一个容器类:
template<class T> class Array
{
public:
Array(int n = 0) :data(new T(n)), sz(n){}
Array(const Array& a)
{
init(a.data, a.sz);
}
~Array(){ delete[] data;}
Array& operator=(const Array& a)
{
if (this != &a)//显示检查自我赋值
{
delete[] data;
init(a.data, a.sz);
}
return *this;
}
Array& operator[](unsigned int n)
{
return data[n];
}
private:
void init(T *, int);
private:
T* data;
int sz;
};
template<class T>void Array::init(T* p, int n)
{
sz = n;
data = new T(n);
for (int i = 0; i < sz; i++)
{
data[i] = p[i];
}
}
设计容器类的过程中要注意很多细节,比如删除delete 和 delete[]、赋值、复制函数中的运用,否则容易出现bug,达不到想要的效果。
//主程序
int main()
{
Array<Obj_Trace> x(3);
Array<Obj_Trace> y = x;
x = y;
return 0;
}
总结:跟踪器只要设计得合适,我们是可以成为验证其他类或者函数运行状况的强大工具,也可以直接放在产品代码中。