xyjzsh

指向类成员对象的指针

定义:
class Shape
{
public:
//...
void moveTo(Point newLocation);
bool validate()const;
vritual bool draw() const=0;
//...
}:
class Circle: public Shape
{
//...
bool draw() const;
//...
};

void (Shape::*mf1)(Point ) = &Shape::moveTo;//指向类的成员函数的指针。

ReturnType (ClassName::*pFuncName)(paramList);
pFuncName定义了指向ClassName中的一组函数。这组函数的形式是返回值为ReturnType,函数列表为paramList.

ClassName的限定使得只有ClassName或派生自它的类的对象才能解引用这个函数指针。

  1 #include"stdafx.h"
  2 #include<stdio.h>
  3 #include<stdlib.h>
  4 
  5 class Base;
  6 class Derived;
  7 typedef long (Base::*PFunc)(long,long);
  8 
  9 enum FuncType
 10 {
 11     ADD=1,
 12     SUB,
 13     DIV,
 14     MUL,
 15 };
 16 
 17 typedef struct tagCallBackFunc
 18 {
 19     long funcType;
 20     PFunc proc;
 21 }COMMAND,*PCOMMAND;
 22 
 23 class Base
 24 {
 25 public:
 26 
 27     virtual PCOMMAND GetCommands(void);
 28     static COMMAND command[];
 29 
 30     long base_add(long a,long b){printf("in base_add()\n");return a+b;}
 31     long base_sub(long a,long b){printf("in base_sub()\n");return a-b;}
 32     long base_div(long a,long b){printf("in base_div()\n");return a/b;}
 33     long base_mul(long a,long b){printf("in base_mul()\n");return a*b;}
 34 };
 35 
 36 COMMAND Base::command[]={
 37                        {ADD,(PFunc)&Base::base_add},
 38                        {SUB,(PFunc)&Base::base_sub},
 39                        {MUL,(PFunc)&Base::base_mul},
 40                        {DIV,(PFunc)&Base::base_div},
 41                   };
 42 PCOMMAND Base::GetCommands()
 43 {
 44     return command;
 45 }
 46 
 47 class Derived:public Base
 48 {
 49 public:
 50     Derived(void){}
 51     ~Derived(void){}
 52     virtual PCOMMAND GetCommands(void){return command;}
 53 
 54     long add(long a,long b){printf("in add()\n");return a+b;}
 55     long sub(long a,long b){printf("in sub()\n");return a-b;}
 56     long div(long a,long b){printf("in div()\n");return a/b;}
 57     long mul(long a,long b){printf("in mul()\n");return a*b;}
 58 
 59     static COMMAND command[];
 60 };
 61 
 62 COMMAND Derived::command[]=
 63                    {
 64                        {ADD,(PFunc)&Derived::add},
 65                        {SUB,(PFunc)&Derived::sub},
 66                        {MUL,(PFunc)&Derived::mul},
 67                        {DIV,(PFunc)&Derived::div},
 68                        {0}
 69                     };    
 70 
 71 
 72 
 73 void  test(Base *control,FuncType funcType,long operand1,long operand2)
 74 {
 75     PCOMMAND pCommand = control->GetCommands();
 76     PCOMMAND pNowCommand=NULL;
 77 
 78     for(long i=0;pCommand[i].funcType;++i)
 79     {
 80         if(funcType == pCommand[i].funcType)
 81         {
 82             pNowCommand = &pCommand[i]; 
 83             break;
 84         }
 85     }
 86 
 87     if(pNowCommand)
 88     {
 89         long res = (control->*pNowCommand->proc)(operand1,operand2);
 90         printf("res=%d\n",res);
 91     }
 92 }
 93 
 94 int main()
 95 {
 96     Derived *=new Derived();
 97     Base *= (Base*)d;
 98     test(b,ADD,1,2);
 99 
100     Base *bb = new Base;
101     test(bb,MUL,1,2);
102     delete bb;
103     delete d;
104     return 0;
105 }

第89行是对指向类成员函数的指针的解析。

posted @ 2010-12-15 14:00 呆人 阅读(290) | 评论 (0)编辑 收藏

数组、函数指针问题

首先说一下数组指针和函数指针的定义问题。
一条重要的规则:函数和数组修饰符的优先级比指针修饰符的优先级高。函数的修饰符为()、数组的修饰符为[]、指针的修饰符为*。
eg:
int *A[N]: 等价于(int*)(A[N]):A[N]表示有N个元素的数组。元素类型为int*
int (*A)[N]:因为有括号提高了*的优先级。所以等价于int ((*A)[N])。表示A是一个指针。A指向的是含有N个元素的数组。数组中存放的元素为int型。


对于指向函数的指针。
int (*afp2[N])(); afp2[N]表示afp2是个含有N个元素的数组。数组中的元素为指向返回值为int、无参数的函数指针。
因为函数的声明较为复杂。可以用typedef来使代码更加明确。
我们用typedef来改写上面的声明。
typedef int (*FP)();//定义FP为返回值为int、参数列表为空的函数的类型。
FP afp2[N];//含有N个元素的数组,返回值为FP。

将一个函数的地址初始化或赋值给一个指向函数的指针时,无需显示的取其地址。编译器知道隐式的取其地址。

void*可以指向任意类型的数据。
不存在可以指向任意类型函数的通用指针。

posted @ 2010-12-15 11:39 呆人 阅读(259) | 评论 (0)编辑 收藏

读写锁的实现

 

#include"stdafx.h"
#include
<windows.h>
/*对于一块临界区
**可以同时读
**当要写时,等待所有的读者离开,后来的读者不允许进入
**因为在同一个进程中
**所以使用临界区,而不是使用互斥对象,因为这样比较节省资源
*/

class __declspec(novtable) auto_RWLock
{
public:
    auto_RWLock(
void);
    
~auto_RWLock(void);

    
void LockForRead(void);
    
void UnLockForRead(void);

    
void LockForWrite(void);
    
void UnLockForWrite(void);

private:
    
long readerCount;
    CRITICAL_SECTION critical_section;
    CRITICAL_SECTION reader_all_leave;
}
;
#include"stdafx.h"
#include
"auto_RWLock.h"

auto_RWLock::auto_RWLock(
void):readerCount(0)
{
    InitializeCriticalSection(
&critical_section);
    InitializeCriticalSection(
&reader_all_leave);
}

auto_RWLock::
~auto_RWLock(void)
{
    DeleteCriticalSection(
&critical_section);
    DeleteCriticalSection(
&reader_all_leave);
}


void auto_RWLock::LockForRead(void)
{
    EnterCriticalSection(
&critical_section);
    
    
if(InterlockedIncrement(&readerCount) ==1)
        EnterCriticalSection(
&reader_all_leave);

    LeaveCriticalSection(
&critical_section);
}


void auto_RWLock::UnLockForRead(void)
{
    
if(InterlockedDecrement(&readerCount)==0)
        LeaveCriticalSection(
&reader_all_leave);
}


void auto_RWLock::LockForWrite(void)
{
    EnterCriticalSection(
&critical_section);
    EnterCriticalSection(
&reader_all_leave);
}


void auto_RWLock::UnLockForWrite(void)
{
    LeaveCriticalSection(
&critical_section);
    LeaveCriticalSection(
&reader_all_leave);
}

posted @ 2010-12-07 11:33 呆人 阅读(1855) | 评论 (2)编辑 收藏

编程习惯(2)

下面的代码片段中红色标出的部分等价于绿色标出的部分:
红色的部分更简洁明了。很适合链表操作中的添加操作。
zwl_srv_control *__list__,*__end__;
void add_job(zwl_srv_control*pcontrol)
 {
  pcontrol->_pnext = NULL;
  EnterCriticalSection(&__cs__);
  (__end__?__end__->_pnext:__list__) = pcontrol;
  /*
  if(!__end__)
   __list__ = pcontrol;
  else
   __end__->next = pcontrol;
  */
  __end__ = pcontrol;
  LeaveCriticalSection(&__cs__);
  Sleep(0);
 }

posted @ 2010-12-07 10:48 呆人 阅读(491) | 评论 (2)编辑 收藏

支持任意个子女的一种树结构

对于任意含有n个节点的有根树仅用O(n)的空间。

是一种左孩子、有兄弟的表示。
每一个节点包含下列属性:
1.指向父节点的指针
2.left-child[x]指向节点x的最左孩子
3.right-sibling[x]指向节点x紧右边的兄弟。

posted @ 2010-12-03 10:20 呆人 阅读(191) | 评论 (0)编辑 收藏

同时求出最大值最小值的方法

在O(3n/2)比较后求出最大值和最小值
思想:成对的处理元素,两者较大的和max比较,两者较小的和min比较。

GetMaxAndMin(A,n)
if n为奇数
max = min = A[0];
i = 1;

else
if A[0]>A[1] then  max = A[0]; min = A[1]
else max = A[1]; min = A[0];
i =2;

for i to n-1

if(A[i]>A[i+1])
{
   max = max>A[i] ? max: A[i];
   min = A[i+1]<min ? A[i+1]:min;
}
else
{
   max = A[i+1]>max ? A[i+1]:max;
   min = A[i]<min ? A[i] : min;
}

i+=2;
   

posted @ 2010-12-02 11:11 呆人 阅读(435) | 评论 (0)编辑 收藏

O(n)的时间内找到第k个最大值(最小值)的算法

下面介绍一种在O(n)的时间内找出第k个最大值(最小值)的方法
该方法和快速排序相似。不同在于每次只出理一边。
伪代码如下:
Random-select(A,p,r,i)//找到A中的第i个最小值
if p==r
then return a[p]
q = random-partition(A,p,r)
k = p-q+1
if(i==k)
then return A[q]
else if i<k
then return random-select(A,p,q-1,i)
else return random-select(A,q+1,r,i-k)
这个算法很不错。

posted @ 2010-12-02 11:02 呆人 阅读(928) | 评论 (0)编辑 收藏

浅谈排序算法

最近在看《算法导论》,首先在这推荐一下这本书,写的确实很精彩
《算法导论》的第二部分分析了多种排序算法。包括插入排序、归并排序、堆排序、快速排序以及线性排序的几个算法。
下面简要总结一下。
对于输入规模为n的数组。插入排序的复杂度为O(n^2)。
归并排序、堆排序、快速排序的复杂度为O(n㏒n);
线性排序的复杂度为O(n)
1.插入排序的性能和输入元素的的序列有很大的关系,如果输入已排序的序列,则复杂度是线性的,若输入是逆序的则是O(n^2)的。
2.堆排序用到了优先队列(优先队列是一种用来维护由一组元素构成的集合S的数据结构)。
3.快速排序的关键是主元的选取(pivot)。在排序过程中元素被分成四部分:小于等于主元的序列、大于主元的序列、未比较的序列、主元。
当未比较的序列未空时,再分别对小于等于主元的序列进行排序、对大于主元的序列进行排序。
以上三种排序都是原地排序。所谓的原地排序(in-place)是指这些元素是在原数组中重排序的,在任何时刻,至多其中的常数个数字是存储在数组之外的。
4.归并排序是将原数组划分成子序列,对子序列进行排序,然后将排序好的子序列合并到一起从而使得原序列重排。
归并排序不是原地排序,它需要额外的内存资源。

以上四种是比较常用的排序,对于输入序列没有特殊的要求,并且都是比较排序,也就是说通过比较各个元素而进行的排序。它们的时间复杂度最好为O(n㏒n);

下面介绍能在时间内完成的排序。
1.计数排序
适用条件:输入序列中的元素取值在一个范围之内0-k
基本思想:对于每一个元素确定比它小的的元素的个数。
排序A序列,将结果放入B中,元素的取值范围为0-k
伪代码如下:
Count-sort(A,B,k)
for i=0 to k
do C[i]=0
for j=1 to length(A)
do C[A[j]] = C[A[j]]+1;计算A[j]的个数。

for i=1 to k
do C[i] = C[i-1]+C[i];计算小于和等于i的元素个数

for j=length(A) downto 1
do B[C[A[j]]] = A[j];
C[A[j]] = C[A[j]]-1;

我们可以明显看到计数排序不是原地排序。同时计数排序是稳定的(即相同的元素输入和输出的相对位置不变。)



posted @ 2010-12-02 11:01 呆人 阅读(242) | 评论 (0)编辑 收藏

c语言之typedef的用法【转载】

c语言之typedef 的用法
用途一:
定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如:
char* pa, pb;  // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针, 
// 和一个字符变量;
以下则可行:
typedef char* PCHAR;  // 一般用大写
PCHAR pa, pb;        // 可行,同时声明了两个指向字符变量的指针
虽然:
char *pa, *pb;
也可行,但相对来说没有用typedef的形式直观,尤其在需要大量指针的地方,typedef的方式更省事。

用途二:
用在旧的C代码中(具体多旧没有查),帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为: struct 结构名 对象名,如:
struct tagPOINT1
{
    int x;
    int y;
};
struct tagPOINT1 p1; 

而在C++中,则可以直接写:结构名 对象名,即:
tagPOINT1 p1;

估计某人觉得经常多写一个struct太麻烦了,于是就发明了:
typedef struct tagPOINT
{
    int x;
    int y;
}POINT;

POINT p1; // 这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时候

或许,在C++中,typedef的这种用途二不是很大,但是理解了它,对掌握以前的旧代码还是有帮助的,毕竟我们在项目中有可能会遇到较早些年代遗留下来的代码。

用途三:
用typedef来定义与平台无关的类型。
比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:
typedef long double REAL; 
在不支持 long double 的平台二上,改为:
typedef double REAL; 
在连 double 都不支持的平台三上,改为:
typedef float REAL; 
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
标准库就广泛使用了这个技巧,比如size_t。
另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)。

用途四:
为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例:

1. 原声明:int *(*a[5])(int, char*);
变量名为a,直接用一个新别名pFun替换a就可以了:
typedef int *(*pFun)(int, char*); 
原声明的最简化版:
pFun a[5]; 

2. 原声明:void (*b[10]) (void (*)());
变量名为b,先替换右边部分括号里的,pFunParam为别名一:
typedef void (*pFunParam)();
再替换左边的变量b,pFunx为别名二:
typedef void (*pFunx)(pFunParam);
原声明的最简化版:
pFunx b[10];

3. 原声明:doube(*)() (*e)[9]; 
变量名为e,先替换左边部分,pFuny为别名一:
typedef double(*pFuny)();
再替换右边的变量e,pFunParamy为别名二
typedef pFuny (*pFunParamy)[9];
原声明的最简化版:
pFunParamy e; 

理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例:
int (*func)(int *p);
首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。
int (*func[5])(int *);
func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。

也可以记住2个模式:
type (*)(....)函数指针 
type (*)[]数组指针 
---------------------------------

陷阱一:
记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如:
先定义:
typedef char* PSTR;
然后:
int mystrcmp(const PSTR, const PSTR);

const PSTR实际上相当于const char*吗?不是的,它实际上相当于char* const。
原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char* const。
简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。

陷阱二:
typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如:
typedef static int INT2; //不可行
编译将失败,会提示“指定了一个以上的存储类”。

posted @ 2010-11-25 17:15 呆人 阅读(301) | 评论 (0)编辑 收藏

do{}while(0)的好处【转】

在C++中,有三种类型的循环语句:for, while, 和do...while, 但是在一般应用中作循环时,我们可能用for和while要多一些,do...while相对不受重视。
    但是,最近在读我们项目的代码时,却发现了do...while的一些十分聪明的用法,不是用来做循环,而是用作其他来提高代码的健壮性。

1. do...while(0)消除goto语句。
通常,如果在一个函数中开始要分配一些资源,然后在中途执行过程中如果遇到错误则退出函数,当然,退出前先释放资源,我们的代码可能是这样:
version 1

bool Execute()
{
   
// 分配资源
   int *= new int;
   
bool bOk(true);

   
// 执行并进行错误处理
   bOk = func1();
   
if(!bOk) 
   {
      delete p;   
      p 
= NULL;
      
return false;
   }

   bOk 
= func2();
   
if(!bOk) 
   {
      delete p;   
      p 
= NULL;
      
return false;
   }

   bOk 
= func3();
   
if(!bOk) 
   {
      delete p;   
      p 
= NULL;
      
return false;
   }

   
// ..........

   
// 执行成功,释放资源并返回
    delete p;   
    p 
= NULL;
    
return true;
   
}


这里一个最大的问题就是代码的冗余,而且我每增加一个操作,就需要做相应的错误处理,非常不灵活。于是我们想到了goto:
version 2

bool Execute()
{
   
// 分配资源
   int *= new int;
   
bool bOk(true);

   
// 执行并进行错误处理
   bOk = func1();
   
if(!bOk) goto errorhandle;

   bOk 
= func2();
   
if(!bOk) goto errorhandle;

   bOk 
= func3();
   
if(!bOk) goto errorhandle;

   
// ..........

   
// 执行成功,释放资源并返回
    delete p;   
    p 
= NULL;
    
return true;

errorhandle:
    delete p;   
    p 
= NULL;
    
return false;
   
}


代码冗余是消除了,但是我们引入了C++中身份比较微妙的goto语句,虽然正确的使用goto可以大大提高程序的灵活性与简洁性,但太灵活的东西往往是很危险的,它会让我们的程序捉摸不定,那么怎么才能避免使用goto语句,又能消除代码冗余呢,请看do...while(0)循环:
version3

bool Execute()
{
   
// 分配资源
   int *= new int;

   
bool bOk(true);
   
do
   {
      
// 执行并进行错误处理
      bOk = func1();
      
if(!bOk) break;

      bOk 
= func2();
      
if(!bOk) break;

      bOk 
= func3();
      
if(!bOk) break;

      
// ..........

   }
while(0);

    
// 释放资源
    delete p;   
    p 
= NULL;
    
return bOk;
   
}


“漂亮!”, 看代码就行了,啥都不用说了...

2 宏定义中的do...while(0)
  如果你是C++程序员,我有理由相信你用过,或者接触过,至少听说过MFC, 在MFC的afx.h文件里面,你会发现很多宏定义都是用了do...while(0)或do...while(false), 比如说:
#define AFXASSUME(cond)       do { bool __afx_condVal=!!(cond); ASSERT(__afx_condVal); __analysis_assume(__afx_condVal); } while(0)
粗看我们就会觉得很奇怪,既然循环里面只执行了一次,我要这个看似多余的do...while(0)有什么意义呢?
当然有!
为了看起来更清晰,这里用一个简单点的宏来演示:
#define SAFE_DELETE(p) do{ delete p; p = NULL} while(0)
假设这里去掉do...while(0),
#define SAFE_DELETE(p) delete p; p = NULL;
那么以下代码:
if(NULL != p) SAFE_DELETE(p)
else   ...do sth...

就有两个问题,
1) 因为if分支后有两个语句,else分支没有对应的if,编译失败
2) 假设没有else, SAFE_DELETE中的第二个语句无论if测试是否通过,会永远执行。
你可能发现,为了避免这两个问题,我不一定要用这个令人费解的do...while,  我直接用{}括起来就可以了
#define SAFE_DELETE(p) { delete p; p = NULL;}
的确,这样的话上面的问题是不存在了,但是我想对于C++程序员来讲,在每个语句后面加分号是一种约定俗成的习惯,这样的话,以下代码:
if(NULL != p) SAFE_DELETE(p);
else   ...do sth...

其else分支就无法通过编译了(原因同上),所以采用do...while(0)是做好的选择了。

也许你会说,我们代码的习惯是在每个判断后面加上{}, 就不会有这种问题了,也就不需要do...while了,如:
if(...)
{
}
else
{
}

诚然,这是一个好的,应该提倡的编程习惯,但一般这样的宏都是作为library的一部分出现的,而对于一个library的作者,他所要做的就是让其库具有通用性,强壮性,因此他不能有任何对库的使用者的假设,如其编码规范,技术水平等。

posted @ 2010-11-24 15:08 呆人 阅读(701) | 评论 (1)编辑 收藏

仅列出标题
共6页: 1 2 3 4 5 6 
<2011年1月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
303112345

导航

统计

常用链接

留言簿(1)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜