消除拷贝构造函数和“模板式拷贝构造函数”中的冗余代码

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

  写完了《当心编译器生成的隐含成员函数》一文,总是对在两个不同的函数中使用相同的代码(只是参数类型不同)做相同的事耿耿于怀。在《C++ STL 中文版》中的quto_ptr中的代码很简单,但很多时候我们要做的并不是这么简单的。代码冗余的危害不只是让你把相同的代码多写一遍,而且会给你的修改带来麻烦——你很容易修改了一个实现而忘记了另一个。赋值操作符的问题很好解决,可以简单地用一个模板成员函数来完成实际的赋值操作,然后在你的拷贝赋值操作符和模板形式的赋值操作符的实现中都只是简单地调用这个成员函数(就是下面的方法一);而拷贝构造函数就相对复杂了一些,因为出于某种理由我们可能需要在成员初始化表中对成员进行初始化而不是在代码中注一,而普通的成员函数是不能使用这种方法的。下面列出几种解决方法,供大家参考:

 

  方法一:使用模板成员函数完成构造。

  就是说使用一个模板成员函数来完成需要在拷贝构造函数中可以放在函数体中的代码部分,然后在拷贝构造函数和“伪拷贝构造函数”注二的实现代码中都只是简单地调用它。代码示意如下:

    template <typename T>

    class smart_ptr

    {

       T* ptr_data;

       template <typename U>

       void DoCopyConstruct(const smart_ptr<U> &)

       {

        ptr_data = Source.get_ptr();

        //其它处理

       }

    public:

       T* get_ptr()const{return ptr_data;}

       smart_ptr(const smart_ptr<T>& Source){DoCopyConstruct(Source);}

 

       template <typename U>

       smart_ptr(const smart_ptr<U>& Source){DoCopyConstruct(Source);}

    };

  这种方法的缺点在于:当有需要在构造函数的成员初始化列表中进行初始化的成员存在时,对这些成员的初始化仍然要在两个构造函数的成员初始化表中各写一遍。不过,一般情况下成员初始化表中的代码都很简单,改动相对较少,所以这里的代码危害也就不是很大。

 

  方法二:使用一个成员对象封装数据。

  事实上,我们对一个对象进行构造实质就是对数据进行初始化,我们可以把数据用一个类封装起来成为一个成员对象,然后在构造函数中在成员初始化表中对这个对象进行初始化。而这时我们在把“伪拷贝构造函数”转移到这个数据类中时可以对它的参数类型进行修改,使它不受拷贝构造函数的影响,比如加一个匿名参数,更简单的方法是把参数类型从引用改为指针。代码示意如下:

  template <typename T>
  class data
  {
    data(const data<T> &);//这个构造函数是不被调用的,写成私有以免误被调用。
  public:
    T* ptr;

    template <typename U>
    data(const data<U> * Source):
    ptr(Source->ptr)
    {
    //其它处理
    }
  };

  template <typename T>
  class smart_ptr
  {
    data<T> ptr_data;
  public:
    const data<T>& get_data()const {return ptr_data;}

    smart_ptr(const smart_ptr<T>& Source):ptr_data(&Source.get_data()){}
 
    template <typename U>
    smart_ptr(const smart_ptr<U>& Source):ptr_data(&Source.get_data()){}
  };

  方法三:把拷贝操作放到基类中实现。

  就是说,为你的类写一个基类,在基类中写一个不受拷贝构造函数影响的模板构造函数,在衍生类中调用这个构造函数。这种方法的原理基本上是和方法二是一样的,只是把组合变成了继承。代码示意如下:

  template <typename T>
  class smart_ptr_base
  {
    smart_ptr_base(const smart_ptr_base<T> &);//这个构造函数是不被调用的,写成私有以免误被调用。
  protected:
    T* ptr;
  public:

    T* get_ptr()const {return ptr}
    template <typename U>
    data(const smart_ptr_base<U> * Source):
    ptr(Source->get_ptr())
    {
    //其它处理
    }
  };

  template <typename T>
  class smart_ptr: protected smart_ptr_base<T>
  {
  public:

    smart_ptr(const smart_ptr<T>& Source):smart_ptr_base(&Source){}
 
    template <typename U>
    smart_ptr(const smart_ptr<U>& Source):smart_ptr_base(&Source){}
  };

  上面只列出了三种方法,但我想解决一个问题的方法是很多的,一定还存在其它方法,如果大家想到了,我希望能够告诉我或补充在这里,由衷感谢。

  此外,上面的示意代码只是针对本文提出的问题的,并没有考虑其它因素,在应用时不能简单照般,还要根据具体情况进行修改。

 

注一:这种理由可以找到很多,比如常量成员的存在、成员无缺省(无参数的)构造函数、效率原因等。

注二:就是题目中的“模板式拷贝构造函数”,我实在不知道该叫它什么名字了,也许称作“模板式拷贝构造函数”更清晰一些,但容易让人误认为它是一个真正的拷贝构造函数,因此在本文中就把它称为“伪拷贝构造函数”

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