martin

thinking

常用链接

统计

software

最新评论

#

模板与泛型算法

前段时间读了一本书《C++沉思录》,偶有感,摘录如下:
模板和泛型算法(摘自 《c++沉思录》):
 
一个特例:
1.假设我们希望从整数数组中找到第一个等于某给定值的元素.编写如下代码:
const int*
find1(const int* array, int n, int x)
{
    const int *p = array;
    for( int i = 0; i < n; i++)
    {
        if(*p==x)
            return p;
        ++p;
    }
    return 0;
}
 
2.泛型化元素类型:
用类型T来表示整型等,适当的时候可以把const也包含在T中,得到如下函数.
template<class T>
T* find2(T* array, int n, constT& x)
{
    T* p = array;
    for(int i=0; i<n; i++)
    {
        if(*p==x)
            return p;
        ++p;
    }
    return 0;
}
   
3.推迟计数.
为了避免预先知道有多少个元素,我们改变函数,使它接受指向第一个元素和最后一个元素之后元素的指针
template<class T>
T* find3(T* array, T* beyond, constT& x)
{
    T* p = array;
    while(p!=beyond)
    {
        if(*p ==x)
            return x;
        ++p;
    }
    return 0;
}
用!=而不用<来判断循环结束并不是偶然.从某种角度来说,两者没有区别,如果find3的输入有意义,则p就小于beyond,直到它们相等为止.但是,由<加以总体排序的类型通常也能用!=来进行比较.另一方面,考虑一下我们以后可能会用到来代替指针的类型,他们可以很好地定义!=,但不一定能定义<.此时,使用<就是一个不合理的假设.
 
另外,我们还假设了,0可以转换成一个与其他所有的值不同的指针值.我们稍微做一点改变,以避免这种假设:如果程序中要找的值没找到,它就返回beyond而不是0.
template<class T>
T* find4(T* array, T* beyond, constT& x)
{
    T* p = array;
    while(p!=beyond)
    {
        if(*p ==x)
            return x;
        ++p;
    }
    return beyond;
}
 
因为程序要么返回适当的T*, 要么返回beyond.故程序代码可以被修改如下:
template<class T>
T* find5(T* array, T* beyond, constT& x)
{
    T* p = array;
    while(p!=beyond && *p != x)
        ++p;
    return p;
}
 
4.地址的独立性
 到目前为止,我们还是依赖于传递来的指针,该指针要指向要查找的数据的开头.但是如果仔细分析一下,会发现我们只依赖于指针的某些保留特性:
1)可以把指针当参数接收,并把它们作为结果返回.
2)可以比较指针是否相等.
3)可以解除引用,以便得到值:*p.
4)可以递增,以指向下一个元素.
 
只要符合上述条件的类型即可,不一定是指针类型.假设把T*作为模板参数,我们就取消了对指针的依赖:
template<class P,class T>
T* find6(P start, p beyond, constT& x)
{
    while(start !=beyond && *start != x)
        ++start;
    return start;
}
 
我们已经完全剔除了函数中关于具体类型的信息.根本没有要求p是指针,只要求p满足上述的四个特性.
 

 

posted @ 2009-03-06 17:43 martin_yahoo 阅读(1803) | 评论 (3)编辑 收藏

C++同步锁管理的一种方法

在C++中,通过提供构造函数、析构函数来对处理资源的获取、释放。
通过C++的这种机制,我们可以很方便地处理C++中的加锁同步机制。把锁对象作为Guard对象的一个成员(m_lock),然后在Guard对象的构造中对m_lock进行加锁:m_lock.acquire(),在Guard对象的析构函数中进行解锁:m_lock.release()。先给出代码实例如下:

template <class T>
class Guard
{
public :
        Guard(const T & lock);
        virtual ~Guard();

private:
        const T & m_lock;
};

template <class T>
Guard<T>::Guard(const T & lock) :
        m_lock(lock)
{
        m_lock.acquire();
}

template <class T>
Guard<T>::~Guard()
{
        m_lock.release();
}

我们可以在应用程序中这样使用它:

 void testFunc(.....)

{

  Guard<MutexWrapper>  guard(mutex);

  ...

}

在刚进入函数testFun(...),创建guard对象,并自动对mutex进行加锁,对特定数据(resource)进行保护。当应用离开testFunc函数模块时,根据guard对象的作用域和生命周期,此时guard对象的析构函数将被调用,因此将自动对mutex进行解锁。在此之后应用的其他线程将可以访问以前被mutex进行保护起来的资源。

 

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

posted @ 2009-03-04 18:11 martin_yahoo 阅读(4124) | 评论 (8)编辑 收藏

C++的一些基础知识

C++中类包含三种成员访问说明符:public, private 和 protected.
在程序能访问类对象的任何地方都可以访问任何在成员访问说明符public后面声明的数据成员和成员函数.成员访问符private后面的数据成员和成员函数只能由该类的成员函数或友元访问.基类的protected成员只能被基类的成员和友元已及派生类的成员和友元访问.
在C++中还存在三中继承方式:public, private, protected.
 
对于它们的论述可以在任意一本关于C++的书中都可以找到.大家对public继承都比较熟悉.但我们偶尔也会看到private继承.private继承时基类中的public,private成员变成派生类中的private成员.基类中的private成员在派生类中隐藏.
 
这里简单介绍一下以下两种情况的异同:
(1)B private 继承A
(2)A是B的一个私有成员的异同.
 
相同点:A的接口(public 成员函数)都只对B开放,不对外开放. 
不同点:在(1)中A的public, protected成员都为B的private成员,B的成员函数可以直接访问.在(2)中A的成员都不是B的成员,并且B不能访问A的protected成员,要访问A的public成员也要借助A的对象.
 
下面再讲一些编译器在构造类时所采取的缺省操作:
1.如果类没有声明构造函数,编译器会声明一个默认构造函数.
2.如果没有声明拷贝构造函数,编译器会隐式地生成一个.
3.如果没有声明赋值操作符,编译器会隐式地生成一个.
4.如果没有声明析构函数,编译器会隐式地生成一个.
 
隐式生成的函数都是public的.
如果接受一个副本是有效的行为,就该声明并定义拷贝构造函数和赋值操作符.如果接受一个副本是禁止的,你可以将拷贝构造函数和赋值操作符声明为private,并且不实现它们,这样可以阻止编译器生成缺省的操作,从而防止客户复制类对象.
 
下面是代码实例:
 
class test{
 
};
 该类中不包含任何成员,也没声明任何方法.编译器会缺省生成下列方法:
test::test()
{
}
 
test::~test()
{
}
 
test::test(const test& rt)
{
 ...
}
 
test& test::operator=(const test& rt)
{
...
}
 
这些方法都是public的.
 
如果想阻止编译器生成缺省的拷贝构造函数,和赋值操作,可以进行如下操作:
class test{
 
private:
    test(test& rt);                        //    该方法被定义为private,并且不被实现.
    test& operator=(test& rt);        //    该方法被定义为private,并且不被实现.
};
 

posted @ 2009-03-04 14:51 martin_yahoo 阅读(1393) | 评论 (2)编辑 收藏

程序中的跟踪方法

在应用开发构成中,我们经常在程序中加入一些打印语句,来对程序的执行流进行跟踪.在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, 则进行输出,否则则不进行输出.
 

posted @ 2009-03-03 16:39 martin_yahoo 阅读(1324) | 评论 (0)编辑 收藏

C/C++混合编程

前段时间,碰到了C,C++混合编程的需求,经过努力,顺利解决问题.现把这方面的知识做一下简单的总结:
 
1.当C++文件要用到C语言中的函数代码时,采用下属方法即可:
在C++中的.h文件或.cpp文件中加入下列代码,
#define LINT_ARGS 1
extern "C" {
#include "system.h"
}
 
然后在代码中直接调用这些函数即可.

注解:
1.1 LINT_ARGS 1表示在检查C语言中的函数原型时,要对函数原型的参数进行检查. 
1.2. "{ }" 中包含的头文件为C语言中的头文件.
1.3.extern "C" 告诉链接器,这些头文件中的函数被当做C语言中的函数来处理.
 
下面以一个实例加以说明:
下面为一个C语言的头文件(sysgct.h):
#ifdef LINT_ARGS
  int  GCT_send(unsigned int task_id, HDR *h);
  ......
#else
  int  GCT_send();
  ......
#endif
~
in file MapBaseReq.cpp 文件中
#include ....
extern "C"
{
#include "sysgct.h"
}
void
MapBaseReq::sendPdu(const BasePdu_var& thePduP)
{
    ...
    if (GCT_send(m->hdr.dst, (HDR *)m) != 0)
        {
                relm((HDR *)m);
                SETERR1(errSWErrorMsg, "sendPdu(): GCT_send() Failed");
        }
   ...
}
 
2.当C文件要用到C++语言某个类中的方法时,可以采用下列方法:
2.1 在cpp文件中用下列方式定义函数:
extern "C" returnType FunName(parameters list).
 
2.2 然后在相应的头文件中进行声明:
extern returnType FunName(parameters list);
 
2.3 在相应的.c文件中包含该头文件,然后直接利用相应的函数即可.
 
下面给出实例.
 
2.4 cpp文件

#include <iostream>
#include <iomanip>
#include "TTDebug.h"
using namespace std;

extern "C"
{
#include "Utility.h"
}

static int display_hex_buffer(unsigned char* buffer, unsigned char* ptr,int len);

extern "C" void tDebug_traceFunc(int level, char* trace)
{
        TDEBUG_TRACEFUNC(level,trace);
}

extern "C" void nDebug(int level, unsigned char* args, int iLen, int cid)
{
        unsigned char buf[512];
        if(0 != TTDebug::instance() && TTDebug::instance()->isTTDebugOn(level))
        {
                /* Check whether the current thread already holds the mutex lock */
                LockGuard<MutexWrapper> guard(TTDebug::instance()->mutex());
                TTDebug::instance()->traceStart(level, __FILE__, __LINE__);
                memset(buf,0,512);
                display_hex_buffer(buf,args,iLen);
                TTDebug::instance()->outStream() << "Send Msg(0-0x" << setw(4) << setfill('0') << hex << cid <<"):0x" << buf;
                TTDebug::instance()->traceEnd();
        }
}

2.5 .h 文件

#ifndef __UTILITY_H
#define __UTILITY_H

extern void tDebug_traceFunc(int level, char* trace);
extern void nDebug(int level, unsigned char* args,int iLen, int cid);

#endif
 
2.6 cpp文件中定义的函数在c文件中调用实例
在test.c文件中:

...

int ctu_ent(msg,pInt)
  MSG* msg;
  int *pInt;
{

 tDebug_traceFunc(10,"ctu ctu_ent");

  HDR *h;
  MSG *m;

   ...

}

...


posted @ 2009-03-03 16:25 martin_yahoo 阅读(4800) | 评论 (3)编辑 收藏

利用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,或数组)只能保存一种类型,另外又要用到运行时的多态,最好保存指向基类对象的指针,而不直接保存对象,否则子对象将被切割,只保留父类部分,其余将被丢弃.另外为减少对对象管理的负担,最好在容器中保存对象的代理对象.

posted @ 2009-03-03 15:10 martin_yahoo 阅读(1460) | 评论 (0)编辑 收藏

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将摧毁本身,从而释放该对象所占用的资源。
通过这种方式,我们就可以把对资源的管理交给机器来管理,解除了对人工的倚赖。
 

posted @ 2009-03-03 15:00 martin_yahoo 阅读(4166) | 评论 (5)编辑 收藏

仅列出标题
共2页: 1 2