2009年3月2日星期一

C++中的程序跟踪方法

在应用开发构成中,我们经常在程序中加入一些打印语句,来对程序的执行流进行跟踪.在C或C++中可以利用下列语句来实现:
(1)
printf("enter %s\n",(char *)funcName);

cout<<"enter "<< s_funcName << endl;

但这样处理有点不足,就是该语句只输出到标准输出上,我有时希望这些输出被定向到特定文件,输出成日志.为此,我们可以把这些函数进行包装,把输出流ostream(标准输出或文件输出)作为包装函数的一个参数:
(2)
printWrap(ostream out,format, args);注:此处的args, format表示要输出的参数和相应的参数格式.
当然我们还可以对它进行进一步的改进:在该函数中,加入预定以的符号常量__LINE__(当前源代码行的行号,为整数常量),__FILE__(假定的源文件名,某个字符串).这样我们可以知道程序运行到了那个源文件,并且那一行.

现在(2)中的处理方式比(1)中处理方式已经有明显的改善了.但这种方式还稍微有点不足.当我们想要跟踪一个函数的执行,即知到执行流进入某函数,何时离开某函数时,这种处理方式有点不足.每个函数都有一个入口,但可能有多个出口,这样就需要在每个入口和出口处加上printWrap(ostream out,args)语句,并且在C++中,当执行流遇到异常退出该函数时,可能有些printWrap语句并没有被执行,从而没有输出记录.

为此,我们可以对(2)进行进一步改进.我们可以设计一个类,在该类对象的构造函数,析构函数中进行输出.在函数的入口处,调用对象的构造函数进行输出;在函数的出口处,或异常退出时,调用对象的析构函数进行输出.
我们可以把该类简单总结如下:
(3)
class Trace{
public:
Trace(int iDebugLevel,ostream out, format,args) { cout <<"Hello\n";}
~Trace() { cout << " Goodby\n";}
int getDebugLevel();
private:
...
int iDebugLevel;
ostream m_out;
};

注: 我们可以用printWrap(..)替换cout << ....。printWrap中的输出流在Trace的构造函数中传到Trace实例中,并被保存。

我们还可以对它进行一点改进,以提高它的性能。因为采用上面的对象。则每次都会进行输出或进行日志记录.我们可以通过构造函数在Trace的实例中,设置一个iDebugLevel变量和ostream。并在系统中设置一个统一的debugLevel.在每次进行输出时进行iDebugLevel, debugLevel比较,如果iDebugLevel <= debugLevel, 则进行输出,否则则不进行输出.

C++中的程序跟踪方法

在应用开发构成中,我们经常在程序中加入一些打印语句,来对程序的执行流进行跟踪.在C或C++中可以利用下列语句来实现:
(1)
printf("enter %s\n",(char *)funcName);

cout<<"enter "<< s_funcName << endl;

但这样处理有点不足,就是该语句只输出到标准输出上,我有时希望这些输出被定向到特定文件,输出成日志.为此,我们可以把这些函数进行包装,把输出流ostream(标准输出或文件输出)作为包装函数的一个参数:
(2)
printWrap(ostream out,format, args);注:此处的args, format表示要输出的参数和相应的参数格式.
当然我们还可以对它进行进一步的改进:在该函数中,加入预定以的符号常量__LINE__(当前源代码行的行号,为整数常量),__FILE__(假定的源文件名,某个字符串).这样我们可以知道程序运行到了那个源文件,并且那一行.

现在(2)中的处理方式比(1)中处理方式已经有明显的改善了.但这种方式还稍微有点不足.当我们想要跟踪一个函数的执行,即知到执行流进入某函数,何时离开某函数时,这种处理方式有点不足.每个函数都有一个入口,但可能有多个出口,这样就需要在每个入口和出口处加上printWrap(ostream out,args)语句,并且在C++中,当执行流遇到异常退出该函数时,可能有些printWrap语句并没有被执行,从而没有输出记录.

为此,我们可以对(2)进行进一步改进.我们可以设计一个类,在该类对象的构造函数,析构函数中进行输出.在函数的入口处,调用对象的构造函数进行输出;在函数的出口处,或异常退出时,调用对象的析构函数进行输出.
我们可以把该类简单总结如下:
(3)
class Trace{
public:
Trace(int iDebugLevel,ostream out, format,args) { cout <<"Hello\n";}
~Trace() { cout << " Goodby\n";}
int getDebugLevel();
private:
...
int iDebugLevel;
ostream m_out;
};

注: 我们可以用printWrap(..)替换cout << ....。printWrap中的输出流在Trace的构造函数中传到Trace实例中,并被保存。

我们还可以对它进行一点改进,以提高它的性能。因为采用上面的对象。则每次都会进行输出或进行日志记录.我们可以通过构造函数在Trace的实例中,设置一个iDebugLevel变量和ostream。并在系统中设置一个统一的debugLevel.在每次进行输出时进行iDebugLevel, debugLevel比较,如果iDebugLevel <= debugLevel, 则进行输出,否则则不进行输出.

2009年3月1日星期日

C++中的对象同步方法

在C++中,通过提供构造函数、析构函数来对处理资源的获取、释放。
通过C++的这种机制,我们可以很方便地处理C++中的加锁同步机制。把锁对象作为Guard对象的一个成员(m_lock),然后在Guard对象的构造中对m_lock进行加锁:m_lock.acquire(),在Guard对象的析构函数中进行解锁:m_lock.release()。先给出代码实例如下:
template class Guard{public : Guard(const T & lock); virtual ~Guard();
private: const T & m_lock;};
template Guard::Guard(const T & lock) : m_lock(lock){ m_lock.acquire();}
template Guard::~Guard(){ m_lock.release();}
我们可以在应用程序中这样使用它:
void testFunc(.....)
{
Guard guard(mutex);
...
}
在刚进入函数testFun(...),创建guard对象,并自动对mutex进行加锁,对特定数据(resource)进行保护。当应用离开testFunc函数模块时,根据guard对象的作用域和生命周期,此时guard对象的析构函数将被调用,因此将自动对mutex进行解锁。在此之后应用的其他线程将可以访问以前被mutex进行保护起来的资源。

利用上面的方法,我们可以包对资源的同步访问和访问控制交给C++的编译器,而不需要进行人工干预,从而减轻应用开发人员的工作负担。

2009年2月26日星期四

利用C++设计基本软件平台

在运用JAVA,C++等面向对象的语言进行开发的时候,不可避免地要用到继承.即从一个父类(P)派生出相应的子类(C).在开发应用的时候,我们可以仅从单个类的角度来考虑继承或派生.但是我们可以进一步对它进行引申.比如我们可以用基类(或纯抽象类,JAVA中的接口)来开发处理某类业务的抽象架构或平台,然后针对具体的应用,我们派生出相应的派生类,让它们来完成具体业务的具体逻辑.
在C++中,基础架购用基类写就,但具体业务逻辑用派生类来实现.为了做到这一点,我们必须在架构中指向基类对象的指针(->操作符),并且定义相应的虚函数(或纯虚函数).这样实现程序的动态多态.这样实现既满足了面向对象设计的OCP原则(open-close principle).

在基础架构中可能还包含保存基类指针的容器,这些指针可能后来所赋之值是派生类的指针,并且考虑到对象的生命周期,这些对象应该是通过NEW操作在heap上生成的对象,而不是在stack上保存的局部对象.为了保证这些对象的自动销毁,不需要应用开发人员的人工干预,这些保存在容器中的指针最好是含有基类指针的智能指针SmartPointer,或者说是代理类.SmartPointer是代理类中的一种.

根据前一篇文章的分析,在应用对对象指针的处理,采用了智能指针.但指向基类(P)的智能指针(SmartPp)与指向子类(C)的智能指针(SmartPc)不是父类与子类的关系,它们应该是同一类的不同实例.因此还应该对智能类定义如下操作,使之满足转型要求:
(1)从智能指针中获取指向对象指针的能力.
(2)根据指向对象的指针生成智能指针的能力.
满足这两点,我们就可以从SmartPc中获取pC(指向子类的指针),然后把它转型成pP(指向父类的指针),然后再根据pP生成SmartPp,然后保存在基础架构的容器中.在实际应用的过程中,会用到指向父类的指针,但此时它实际上是指向子类的指针,在程序运行的过程中,将用到动态多态性,即虚函数来处理相应的应用.

BTW(顺便说一下),因为一般说来容器(MAP,vecotr,或数组)只能保存一种类型,另外又要用到运行时的多态,最好保存指向基类对象的指针,而不直接保存对象,否则子对象将被切割,只保留父类部分,其余将被丢弃.另外为减少对对象管理的负担,最好在容器中保存对象的代理对象.

C++中的智能指针

1.浅论C++中的智能指针(Smart Pointer)
简单地讲,智能指针是用一个对象来对指针进行建模,使之具有指针的特性,跟指针具有相同含义的->,*操作.并且通过对象的构造函数(获取资源),析构资源(释放资源)来对资源进行管理,从而减少程序员对通过new操作获取到的对象的生命周期进行管理的负担.
根据《Moden C++ Design》, 我们可以构造具有很多正交特性的智能指针。
1.1  C++中的智能指针与JAVA中的对象
前段时间跟朋友聊了些有关JAVA的东西,感觉上Java中的对象就是C++中的智能指针,但具有不同的资源释放方式。在JAVA中,不能象C++中运用" A a;"语句声明得到一个类(A)的事例a,而必须通过下列语句来获得:Aa = new A.要在释放a时,应用必需通知
GC(垃圾收集功能)来释放该实例所占用的资源。当然,JAVA中的对象有一小点同C++中的职能智能不同,因为在C++中指针不具有"."操作符,故智能指针一般也不提供"."操作符,但在Java中都是通过"."操作符对对象进行操作的,不过我们可以把C++中职能指针的"->"操作符与
Java中的"."操作符进行类比。
1.2 引用计数型智能指针
在C++中有一种常用的智能指针是引用计数型智能指针:RCSmartPtr. 它的实现基理如下:
首先,存在RCObject,即存在一个对象,该对象提供引用计数接口。
另外,要存在指向RCObject的RCSmartPtr对象,在RCSmartPtr对象的构造过程中,把指向RCObject的指针作为参数传入RCSmartPtr中。因此每增加一个RCSmartPtr对象,就多了一个指向RCObject的指针。RCSmartPtr可以通过调用RCObject的引用计数接口,增加RCObject
的引用计数。同样的道理可以在RCSmartPtr对象的析构函数中调用RCObject的引用记数接口来减少RCObject的引用记数。
第三,在对RCObject的引用计数进行操作时对引用计数进行检查,如果引用计数为0,则RCObject将摧毁本身,从而释放该对象所占用的资源。
通过这种方式,我们就可以把对资源的管理交给机器来管理,解除了对人工的倚赖。

2009年2月19日星期四

Hello

Hello,

This is my first test of blog.