C++ 复制构造函数和运算符

C++ Copy constructor and Operator

我试图理解 Copy constructorOperator。我读了一些代码,但我就是不明白。

这里是主要函数

int main()
{
Cwin win1('A',"window")
Cwin win2;
win1 = win2;
win1.set_data('B',"hello");
win1.show();
win2.show();
}

我从ClassCwin

中提取最重要的代码
Class Cwin
{
  private:
  char id, *title ;

  public:
  Cwin(char i, char *text){......} //constructor
  Cwin(){......}  //default constructor, id ='D' , *title = "default"
  Cwin(const Cwin &win)
  {
    id = win.id;
    strcpy(title,win.title);
  }
  ......
};

输出

B : hello
B : hello

我能理解是什么原因造成的。但我无法理解以下修复它的解决方案。

Class Cwin 
{
   private:
   char id, *title ;

   public:
   Cwin(char i, char *text){......} //constructor
   Cwin(){......}  //default constructor, id ='D' , *title = "default"
   Cwin(const Cwin &win)
   {
     id = win.id;
     strcpy(title,win.title);
    }

   void operator=(const Cwin &win)
   {
     id = win.id;      
     strcpy (this->title , win.title);
   }
   ......


 };

输出:

B : hello
D : default

为什么把它 strcpy (title , win.title); 改成 strcpy (this->title , win.title); 会有很大的不同?

解释:

成员函数内部可以写this->titletitle。两者是等效的,除非您有一个名为 title 的局部函数变量,然后该名称将隐藏该成员。

不同之处在于您添加了赋值运算符。

如果没有赋值运算符,您的语句 win1=win2 通过逐个成员复制来进行。在这种情况下,一个指针被原样复制,所以在复制之后,两个对象将指向同一个指针 char*:

当您 set_data() 时,字符串被 win1 复制到 char* 指针,但 win2 指向相同的值。

你的第二个代码片段,更好地处理复制:指向的字符串的内容是 copied/duplicated 到 char* 另一个对象指向的。 win1win2 将因此继续使用 2 个不同的字符串。

Remarks/advices

  • 您使用 strcpy() 时未检查目标是否足够长以包含复制的字符串。这可能会导致缓冲区溢出、内存损坏和许多其他可怕的事情。

  • 我强烈建议您尽可能使用 std::string 而不是 char*:默认副本会得到很好的管理,而您没有照顾记忆 allocation/deallocation,length.

如果 Cwin 没有显式地实现它自己的赋值运算符(您的第一个示例没有),编译器将生成一个默认实现,它只是将成员值按原样从一个对象复制到另一个对象。这就是为什么你的输出对两个对象说同样的事情 - 使用默认赋值运算符实现的 win1 = win2 语句只是用 win2.title 的指针值覆盖 win1.title 的指针值,并且因此,两个title成员都指向同一个内存块,随后的win1.set_data()语句填充了数据。

要完成您的要求,您应该将 title 更改为使用 std::string 而不是 char*,让它为您处理复制。它适用于编译器的默认复制构造函数和赋值运算符实现(除非您有其他数据需要手动复制):

#include <string>

Class Cwin
{
private:
  char id;
  std::string title;

public:
  Cwin()
    : id('D'), title("default") {}

  Cwin(char i, const std::string &text)
    : id(i), title(text) {}

  void set_data(char i, const std::string &text)
  {
    id = i;
    title = text;
  }

  void show()
  {
    std::cout << id << " : " << title << std::endl;
  }
};

但是,如果您需要直接使用 char*,则必须正确实现复制构造函数和赋值运算符,以确保正确复制您的 title 数据,例如:

#include <cstring> 

Class Cwin
{
private:
  char id, *title ;

public:
  Cwin()
    : id(0), title(NULL)
  {
    set_data('D', "default");
  }

  Cwin(char i, char *text)
    : id(0), title(NULL)
  {
    set_data(i, text);
  }

  Cwin(const Cwin &win)
    : id(0), title(NULL)
  {
    set_data(win.id, win.title);
  }

  ~Cwin()
  {
    delete[] title;
  }

  Cwin& operator=(const Cwin &win)
  {
    if (this != &win)
      set_data(win.id, win.title);
    return *this;
  }

  void set_data(char i, char *text)
  {
    int len = std::strlen(text);
    char *newtitle = new char[len+1];
    std::strcpy(newtitle, text);

    delete[] title;
    title = newtitle;
    id = i;
  }

  void show()
  {
    std::cout << id << " : " << title << std::endl;
  }
};

任何时候您必须在构造函数中手动分配内存并在析构函数中释放它,很可能您还需要在复制构造函数和赋值运算符中复制该数据。阅读有关 The rule of three/five/zero 的内容。在这种情况下,CWin 需要遵循 三规则 才能与 char* 一起正常运行,但可以遵循 零规则使用 std::string。您应该始终努力编写尽可能遵循 零规则 的代码,这会使事情更易于管理。

C++ classes 有一个默认的赋值运算符,每当您将 class 的一个成员分配给另一个成员时,它会执行 shallow copy 除非您重载它。 (初始化时除外,它使用复制构造函数)

现在,在第一部分的代码中没有赋值运算符的重载,因此它所做的是将 win2 的元素浅拷贝到 win1,后者又拷贝了 'id'和 'title' 指针(不是它指向(存储)的字符串)。 主要功能将运行如下:

int main()
{
  Cwin win1('A',"window")
  /*
    This will create a new object win1
    win1.id = 'A' and win1.title will point to "window"
  */
  Cwin win2;
  /*
    This will create a new object win2
    win2.id = 'D' and win2.title will point to "default"
  */

  win1 = win2;
  /*
  This will do somewhat the following
  win1.id = win2.id;
  win1.title = win2.title; ( note that  the pointer is copied )
  Now , win1.id = 'D' win2.id = 'D'
  win1.title points "Defalult"
  win2.title points "Default"
  */

  win1.set_data('B',"hello");
  /*
  This sets the win.id = 'B'
  and now the win1.title points to "hello"
  Also since win1.title and win2.title are both the same pointer
  so win2.title will also point to  "hello"
  */
  win1.show();
  win2.show();
}

但是在第二部分中,您已经重载了赋值运算符,它不是复制指针而是复制它指向的字符串(存储)。现在主要 运行 如下:

int main()
{
  Cwin win1('A',"window")
  /*
    This will create a new object win1
    win1.id = 'A' and win1.title will point to "window"
  */
  Cwin win2;
  /*
    This will create a new object win2
    win2.id = 'D' and win2.title will point to "default"
  */

  win1 = win2;
  /*
  This will now do what is in the overloaded assignment operator
  where to copy the string strcpy is used which will copy the content
  of the string instead of copying the pointers
  Now , win1.id = 'D' win2.id = 'D'
  win1.title points "Defalult"
  win2.title points "Default"
  */

  win1.set_data('B',"hello");
  /*
  This sets the win.id = 'B'
  and now the win1.title points to "hello"
  win2.title will still point to  "Default"
  */
  win1.show();
  win2.show();
}

因此,给出的结果。 您还必须查看并遵循此 .

中给出的建议