Prefer C++(一)

类别:VC语言 点击:0 评论:0 推荐:

Prefer C++

Written by 李智勇

前言

读了《Eric Raymond对于几大开发语言的评价》一文,觉得其对C++的评价极其偏颇。C++本身支持多典范设计,也就是说你可以完全不用OO,GP而只采用结构化的方法去进行程序设计。这个时候同C相比,效率是基本一致的。而确可以享受到更严格的类型系统检查、inline函数、名字空间、运算符重栽所带来的益处。如果你牺牲一点效率,你还可以使用异常处理。

我以前为项目组成员写过一篇文章,号召大家从C转向C++。现贴出此文,望方家斧正。

 

1、比C更严格的类型系统

        char szBuf[MAX_PATH];

        WORD j=szBuf;

        在C里面是允许的是个警告,而在C++里面则非法是错误。

        而WORD Add(WORD a,WORD b);

           Add(1,szBuf);可能导致灾难性的后果。

更关键的是处理回调函数时:

        比如:API函数原型int SetAbortProc(HDC hdc,  ABORTPROC lpAbortProc);

                     调用时有SetAbortProc(hdcPrn, AbortProc);

如果AbortProc与ABORTPROC类型不符,在c里边将自动转换类型。绝对可能导致灾难性的后果。此时堆栈会如何?

总之C++在防范失误所引起的错误方面做的更好,但绝对不禁止故意打破类型系统的行为。由于必须兼容C,C++做的也不好,警告后还是要做类型转换的。看下面这个例子:

    int a=0;

    unsigned b=0;

    int i;

    for(i=10; i>=b; --i)

        {

         a++;

        }

 

 

 

其实这是个死循环。问题出在i和b类型不匹配(编译时这是个警告,而不是错误)。当必须进行比较时,都会被转成unsigned,于是i>=b将永远为true。

 

2、智能化的资源管理

利用C++的构造和析构函数可以建立非常完善的资源管理机制,尽可能的防止资源泄露。看下面的程序段:

void func1()

{

     char* szBuf=malloc(50);//利用C中void* 到其他类型指针的自动转换

 

     --------

 

     free(szBuf);

     return ;

}

   这样在分配和释放内存之间的任何一个地方,如果需要返回,则必须有这种形式的代码:

if(---)

{ free(szBuf); return 0;}

而一旦忘记释放内存而返回,必将产生内存泄露。

 

C++类的实例有这样一种特性。在其作用域开始的时候,构造函数被调用,超出作用域时析构函数被调用。利用这一规律来管理资源的最有名的例子是标准库中的智能指针(参看:More Effevtive C++,The C++ Standard Library及The C++ Programming Language。

下面简要说明其原理:

Class A;

void func2()

{

    A  a; //构造函数被调用

    ------

    return; //析构函数被调用

}

如果在Class A的构造函数中完成相关资源分配,在析构函数中释放内存会怎样?

分配内存后再不用担心释放的问题了。

 

A大致有如下的形式:

typedef struct tagPoint

{

    int x;

    int y;

}Point,PPoint;

 

Class A

{

    PPpoint pointer;

    SmartPointer(PPpoint p)

    {  

        pointer=p;

    }

 

    ~SmartPointer()

    {

        if(pointer)

        delete pointer;

    }

 

    Point & operator*() const

    {

        return *pointer;

    }

    Point * operator->() const

    {

        return pointer;

    }

};

这样用到Point时就可以这样:

A pointer(new Point);//为Point结构分配了内存

pointer->x=5;        //存取相关元素

//超出作用域时为Point分配的内存将被自动删除

 

而为每一个结构都写一个类不划算,所以人们发明了模板。应用模板时A被定义成这个样子:

 

template <typename T> class SmartPointer

{

    SmartPointer(T* p)

    {  

        pointer=p;

    }

 

    ~SmartPointer()

    {

        if(pointer)

        delete pointer;

    }

 

    T& operator*() const

    {

        return *pointer;

    }

    T* operator->() const

    {

        return pointer;

    }

 

private:

    T* pointer;

};

这样不管什么结构,都可以用A了。

使用时是这样:

A<Point> pointer(new Point);

pointer->x=5;

//超出作用域时为Point分配的内存将被自动删除

 

 

N多人阐述过这个观点,但我觉得在windows下最有用的是附件1。

可实现对Windows下各种句柄的智能化管理。

作者:Jeffey Richter 2000年第四期msdn杂志,win32 Q&A专栏

 

读懂这个例子需要一点模板和符号重载的知识。

用起来很简单:

比如对于文件句柄,有:

CensureCloseHandle hHandle=CreateFile(---);

WriteFile(hHandle);

----------

不用调用CloseHandle了,超出作用域它将被自动调用。

 

最嚣张的应用是有引用记数的智能指针。

见附件2。(Come from the C++ standard library)

3、强大的inline(C不支持这个吧!)

第一次看Link-time Code Generation(Matt Pietrek MSDN magazine 2002 5别的也没记住,就记了个通过链接器自主选择那些函数可以做inline可大幅度提高性能。后来自己就在vc++上试了一下,结果还是真恐怖,先说性能最多能提高多少,看下面的程序,并猜猜inline函数所花费的时间:

int add(int a,int b)

{

       return a+b;

}

 

inline int addinline(int a,int b)

{

       return a+b;

}

 

都是以这种形式进行调用:

    int b;

    for(int j=0;j<50000;j++)

    {

        b=add(60,70);

    }

假设add()所花费的时间是:88964ns(vc++ 6.0 release 版本下),那么addinline所花费的时间是多少?

 

 

真实结果是0,也就是说addinline函数根本不存在。被优化后,剩下的只是一个值0x82。当然这是一个极端的例子,但从另一个角度讲把适当的函数做成inline实际上是给编译器,连接器做出更大优化的机会。

 

inline的另一个优势。

大家都知道,函数调用要处理堆栈(很少是寄存器)。这样就有一部分进栈和出栈的指令,这部分代码对于inline是没有的。

for(int j=0;j<50000;j++)

       {

              b=add(j,j+1);

       }

for(int p=0;p<50000;p++)

       {

              b=addinline(p,p+1);

       }

cout<<b;//必须使用一下b

此时所耗费的时间两者分别为:

1.10619e+006ns 和331754ns

 

 

本文地址:http://com.8s8s.com/it/it1767.htm