复制构造函数为点

时间:2011-07-13 21:04:01

标签: c++ constructor copy point

这个复制构造函数是否正确?

class GPSPoint
{
   private:
      double lat, lon, h;
      char *label;

   public:
    GPSPoint (const GPSPoint &p)
    {
      if (this != &p)
      {
          lat = p.lat;
          lon = p.lon;
          h = p.h;

          if ( label != NULL )
          { 
              delete [] label;
              label = NULL;
          }

          if (p.label != NULL )
          {
              label = new char [ strlen( p.label ) + 1 ];
              strcpy ( label, p.label );
          }
       }
    }
}

6 个答案:

答案 0 :(得分:8)

如果你班上有指针,你可能做错了。

最好将其重写为:

class GPSPoint
{
   private:
      double      lat;
      double      lon;
      double      h;
      std::string label;

   public:
    GPSPoint (GPSPoint const& copy)
       : lat(copy.lat)
       , lon(copy.lon)
       , h(copy.h)
       , label(copy.label)
    {}
    // You should also have an assignment operator
    GPSPoint& operator=(GPSPoint copy)    // Use Copy/Swap idum.
    {                                     // Pass by value to get implicit copy
         this->swap(copy);                // Now swap
         return *this;
    }
    void swap(GPSPoint& copy) throw()
    {
        std::swap(lat,  copy.lat);
        std::swap(lon,  copy.lon);
        std::swap(h,    copy.h);
        std::swap(label,copy.label);
    }
};

现在看起来更简单了。

但是,嘿,我们忘了有编译器生成的副本构造函数:
所以它现在也简化了:

class GPSPoint
{
   private:
      double      lat;
      double      lon;
      double      h;
      std::string label;
};

完成。越简越好。

如果你必须绝对保留指针(因为你认为它是一个优化(不是),或者你需要学习指针(你这样做,但你需要学习何时不使用它们)。)

class GPSPoint
{
   private:
      double      lat;
      double      lon;
      double      h;
      char*       label;

   public:
   // Don't forget the constructor and destructor should initialize label
   // Note the below code assumes that label is never NULL and always is a
   // C-string that is NULL terminated (but that each copy creates 
   // and maintains its own version)  
   GPSPoint (GPSPoint const& copy)
       : lat(copy.lat)
       , lon(copy.lon)
       , h(copy.h)
       , label(new char[strlen(copy.label)+1])
    {
       std::copy(copy.label, copy.label + strlen(label) + 1, label);
    }
    // You should also have an assignment operator
    GPSPoint& operator=(GPSPoint copy)    // Use Copy/Swap idum.
    {                                     // Pass by value to get implicit copy
         this->swap(copy);                // Now swap
         return *this;
    }
    void swap(GPSPoint& copy) throw()
    {
        std::swap(lat,  copy.lat);
        std::swap(lon,  copy.lon);
        std::swap(h,    copy.h);
        std::swap(label,copy.label);
    }
};

答案 1 :(得分:3)

有点冗长;我会重新设计它。

更重要的是,它看起来更像是op=而非复制构造函数,尤其是因为您为label测试NULL,就好像它已经被使用过一样。而且,由于它没有初始化,因此NULL已经不可能...... delete [] label是一个严重错误。

如果您将其设为op=,那么我认为这在语义上是正确的。

那么不要忘记你的构造函数(并初始化labelNULL!),复制构造函数(使用op=)和析构函数。


我知道你在上一个问题(没有任何令人信服的理由)中说过你“不想使用std::string”,但这是你为什么要这样做的完美例子。

答案 2 :(得分:1)

据我了解,您创建了operator =的有效实现,而不是复制构造函数。 如果对象已经存在,if (this != &p)是有意义的;删除标签

相同

答案 3 :(得分:1)

简而言之:不。这不是一个可怕的赋值运算符,但它作为复制构造函数被破坏了。

首先,没有可能的方式进行自我分配,因为你没有分配任何东西。 this指向构造函数开头的未初始化的内存块,而p是您正在复制的完全构造的Point对象。两者不能重合。所以最初的检查是浪费时间。

再往下,检查以确保label不为空。正如我所说,this指向未初始化的内存... label此时可以是任何值,无法判断该测试是通过还是失败。如果它确实不是null,那么你将delete[]一个随机的内存部分。如果你很幸运,这将立即失败,但它没有。

在样式方面,首选构造函数初始化程序列表以初始化成员。

GPSPoint (const GPSPoint& copy)
  : lat(copy.lat)
  , lon(copy.lon)
  , h(copy.h)
  , label(0)
 {
   if(copy.label) {
       label = new char[ strlen( copy.label ) + 1 ];
       strcpy ( label, copy.label );
   } 
 } 

就正确性而言,摆脱c-string,并使用正确的字符串类。然后,您根本不需要实现复制构造函数。

无论如何,如果实现了复制构造函数,请确保实现了复制赋值运算符和析构函数;我认为这些都是为了简洁而遗漏的,但如果不是,那么它们非常重要。

答案 4 :(得分:0)

正如a1ex07在评论中所说,您的代码看起来更像是放在operator=中的内容,而不是复制构造函数。

首先,复制构造函数正在初始化一个全新的对象。类似if (this != &p)的检查在复制构造函数中没有多大意义;您传递给复制构造函数的点永远不会是您在该点初始化的对象(因为它是一个全新的对象),因此检查始终为true

此外,if (label != NULL)之类的操作不起作用,因为label尚未初始化。此检查可能会以随机方式返回truefalse(具体取决于未初始化的label是否NULL)。

我会这样写:

GPSPoint(const GPSPoint& p) : lat(p.lat), lon(p.lon), h(p.h) {
    if (p.label != NULL) {
        label = new char[strlen(p.label) + 1];
        strcpy(label, p.label);
    }
    else {
        label = NULL;
    }
}

也许让label成为std::string而不是C风格的char*会是一个更好的主意,然后你可以纯粹使用初始化列表编写你的复制构造函数:

class GPSPoint {
private:
    double lat, lon, h;
    std::string label;

public:
    // Copy constructor
    GPSPoint(const GPSPoint& p) : lat(p.lat), lon(p.lon), h(p.h), label(p.label) { }
}

答案 5 :(得分:-1)

是的 - 我最初读错了。

但是,我仍然建议您使用std::string作为标签,因为它将为您管理内存,并且复制构造函数变得更加简单(事实上,对于这种情况不需要)。