zelda

 

Symbian C++ 的 NewL ConstructL NewLC ELeave (转)

      初学Symbian开发,第一件感觉迷惑的事情是CleanupStack 第二件肯定是随处可见的NewL,NewLC,ConstructL。这些函数的出现依然和内存泄漏有关,这是一种被称为两步构造的机制,英文叫Two-phase Construction。
      我知道C++里面的 new 操作符实际上完成2件事,第一根据对象类的大小在堆上分配一块内存并获得指向内存的指针,第二利用指针调用类的构造函数,最后把指针返回。在Symbian上这样做是有隐患的,就是当你分配好了内存,但是调用构造函数的时候程序意外退出了,这样会造成刚才分配的内存产生泄漏。只有那些放入CleanupStack的内存,在程序意外结束后会被释放,new 分配的内存在调用构造函数之前还没有被放入CleanupStack呢。(这是原理)

      Symbian的设计师为了解决这个问题,给所有开发者设计了一个编写程序的定式,这就是Two-phase Construction。

具体是这样的:

      把普通的new 操作分为2个步骤来进行,第一步只分配内存,当分配的内存被放入cleanupstack后,第二步进行构造。但是在C++里 如何阻止编译器的new操作不调用构造函数呢?这个貌似不可以。。。于是Symbian的设计者作了个规定,类在构造函数里不要做任何可能产生异常的操作,只能做那些绝对安全的事情,比如简单的变量赋值,然后提供一个名字叫 ConstructL的函数,在这个函数里做所有类的初始化工作,当然包括那些危险的可能导致异常的操作。

那么Symbian的 类构造就变成了这样,比如有一个叫CFoo 的类,我想声明一个指针p,创建一个CFoo的对象赋给p。
      CFoo *p = new(ELeave) CFoo();
      CleanupStack::PushL(p);
      p->ConstructL();
      CleanupStack::Pop();

这样写是不是有点太罗嗦?每个对象都要用4条语句创建,如果是频繁使用实在是太麻烦了。于是Symbian的设计者又作了一个规定,每个类要实现一个NewL的static函数来完成上面的4条语句的工作
      class CFoo
      {
      public:
          static CFoo *NewL()
          {
                CFoo *self = new(ELeave) CFoo();
                CleanupStack::PushL(self);
                self->ConstructL();
                CleanupStack::Pop();
                return self;
           }
      }

有了NewL以后,调用CFoo的类的程序简化了
CFoo *p = CFoo::NewL();

那么NewLC又是什么呢?和NewL有什么不同?有些类是这样的,他们提供一些方法,需要在对象创建完成后执行,但是这些方法也是会产生异常的比如CFoo 如果有一个方法叫 DoSomethingL()。那么程序可以这样写吗?
      CFoo *p = CFoo::NewL();
      p->DoSomethingL();

显然这样写是有问题的正确的写法是
      CFoo *p = CFoo::NewL();
      CleanupStack::PushL(p);
      p->DoSomethingL();
      CleanupStack::Pop();

天啊,又是4条语句,太麻烦了。要知道在NewL结尾我们刚刚把CFoo的指针从CleanupStack里拿出来,马上就又放了进去。是不是可以简化呢,那好我们再节约2条语句:NewL去掉结尾的CleanupStack::Pop();
      static CFoo *NewL()
      {
            CFoo *self = new(ELeave) CFoo();
            CleanupStack::PushL(self);
            self->ConstructL();
            return self;
      }
调用去掉CleanupStack::PushL
      CFoo *p = CFoo::NewL();
      p->DoSomethingL();
      CleanupStack::Pop();

      Symbian的设计者又规定了,具有以上行为的NewL 应该叫NewLC。表示指针返回后,没有从CleanupStack里取出,你可以继续调用一个危险的操作,在最后调用CleanupStack::Pop();我发现 NewL 可以通过调用NewLC实现。
      那么一个符合Symbian设计师的N条规定的类应该这样写

      class CFoo
      {
       public:
             static CFoo *NewLC()
            {
                   CFoo *self = new(ELeave) CFoo();
                   CleanupStack::PushL(self);
                   self->ConstructL();
                   return self;
            }

            static CFoo *NewL()
           {
                   CFoo *self = NewLC();
                   CleanupStack::Pop();
                   return self;
           }

            virtual ~CFoo()
           {
            }

      protected:
           CFoo()
          {
           }

           void ConstructL()
          {
                 // ....
          }
      }

      这里注意 CFoo的构造函数不能是Public的,为了防止使用者用new或者在栈上创建对象。析构函数要写成虚函数,这是纯C++问题,不明白的去看 More Effective C++。要说明一下,以上的写法是Symbian极力推荐的,但是不是硬性规定的,你只要保证没有内存泄漏
可以不这么写。我个人还是推荐这样,这样的代码写Symbian程序的人都可以很好地理解。

      最后说一下 new 之后为什么要有一个 (ELeave)。new操作符是被Symbian重载过了,ELeave是给new的一个参数,他的意思是告诉new当无法分配内存时程序就退出。比如内存不足的时候。所以我们用了ELeave的话 就不用检查new 返回的指针了,能返回就一定是对的如果出了错程序就结束掉了,new根本就不会返回。

NewL NewLC 是Symbian程序标志性的函数,所以有个Symbian开发的资源站点就叫 http://www.newlc.com/

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wxlkeepmoving/archive/2010/05/10/5574752.aspx

posted on 2010-08-09 21:03  zelda  阅读(435)  评论(0编辑  收藏  举报

导航