点的复制构造函数

发布于 2024-11-23 16:41:05 字数 562 浏览 1 评论 0原文

这个复制构造函数正确吗?

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 );
          }
       }
    }
}

Is this copy constructor correct?

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 );
          }
       }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

贱贱哒 2024-11-30 16:41:05

如果你的类中有一个指针,那么你可能做错了什么。

最好将其重写为:

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);
    }
};

If you have a pointer in your class you are probably doing something wrong.

It would be better to re-write it as:

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);
    }
};

Now that looks a lot simpler.

But hey we forgot there are compiler generated copy constructors:
So it now simplifies too:

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

Done. The simpler the better.

If you must absolutely keep the pointer (because you think it is an optimization (its not), or you need to learn pointers (you do, but you need to learn when not to use them)).

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);
    }
};
箹锭⒈辈孓 2024-11-30 16:41:05

有点啰嗦;我会重新设计它的风格。

更重要的是,它看起来更像是 op= 而不是复制构造函数,特别是因为您测试 labelNULL 性,就好像它本来可以是已经用过。而且,由于它尚未初始化,它不太可能已经是 NULL...delete [] label 是一个严重错误。

但是如果你把它作为你的op=,那么我想这在语义上是正确的。

因此,不要忘记您的构造函数(并将 label 初始化为 NULL!)、复制构造函数(使其使用 op=)和析构函数。


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

It's a bit verbose; I'd re-style it.

More importantly, it looks more like an op= than a copy constructor, especially because you test label for NULLness as if it could have been used already. And, since it's not initialised, it's unlikely to be NULL already... delete [] label is then a critical error.

But if you made this your op=, then I guess that would be semantically correct.

So then don't forget your constructor (and initialise label to NULL!), copy constructor (make it use op=) and destructor.


I know you stated in your previous question (without any convincing rationale) that you "don't want to use std::string", but this is a perfect example of why you really should.

流云如水 2024-11-30 16:41:05

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

In my understanding you created a valid implementation of operator =, not a copy constructor.
if (this != &p) makes sense if the object already exists ; the same for deleting label

说不完的你爱 2024-11-30 16:41:05

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

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

再往下,您检查以确保 label 不为空。正如我所说,this 指向未初始化的内存...此时label 可以是任何值,无法判断该测试是否会通过或失败。如果它确实产生不为空,您将删除[]内存的随机部分。如果幸运的话,这会立即失败,但并非必须如此。

就风格而言,更喜欢构造函数初始值设定项列表来初始化成员。

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 字符串,并使用适当的字符串类。然后,您根本不需要实现复制构造函数。

无论如何,如果您实现复制构造函数,请确保实现复制赋值运算符和析构函数;我认为这些内容是为了简洁而被省略的,但即使不是,它们也非常重要。

In short: no. That's not a horrible assignment operator, but it is broken as a copy constructor.

First, there is no possible way for self-assignment to occur, because you are not assigning anything. this points to an uninitialized blob of memory at the start of your constructor, while p is a fully constructed Point object that you are copying. The two can not coincide. So the initial check is a waste of time.

Further down, you check to ensure label is not null. As I said, this points to uninitialized memory... label can be any value at this point, there is no telling whether or not that test will pass or fail. If it does yield not null, you are going to delete[] a random part of memory. If you are lucky, this will fail immediately, but it doesn't have to.

In terms of style, prefer constructor initializer lists for initializing members.

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 );
   } 
 } 

In terms of correctness, get rid of the c-string, and use a proper string class. Then, you won't need to implement a copy constructor at all.

No matter what, if you implement a copy constructor make sure you implement a copy assignment operator and a destructor; I assume those were left out for brevity, but if not they are terribly important.

葵雨 2024-11-30 16:41:05

正如 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) { }
}

As a1ex07 says in a comment, your code looks more like what you would put in operator= than in a copy constructor.

First of all, a copy constructor is initializing a brand new object. A check like if (this != &p) doesn't make much sense in a copy constructor; the point you are passing to the copy constructor is never going to be the object that you are initializing at that point (since it's a brand new object), so the check is always going to be true.

Also, doing things like if (label != NULL) is not going to work, because label is not yet initialized. This check might return true or false in random ways (depending if the uninitialized label is NULL by chance).

I would write it like this:

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;
    }
}

Maybe making label a std::string instead of a C-style char* would be a better idea, then you could write your copy constructor purely using an initializer list:

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) { }
}
我是有多爱你 2024-11-30 16:41:05

是的——我最初读错了。

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

Yes -- I originally read it incorrectly.

However, I still suggest you use std::string for the label, as it will manage the memory for you and the copy constructor becomes much simpler (in fact, unnecessary for this case).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文