C++复制构造函数和算子

C++复制构造函数和算子,c++,class,pointers,operators,copy-constructor,C++,Class,Pointers,Operators,Copy Constructor,我试图理解复制构造函数和运算符。我读了一些代码,但就是不明白 这里是主要功能 我从类Cwin中提取最重要的代码 输出: 我能理解是什么引起的。但我无法理解下面的解决方案 Class Cwin { private: char id, *title ; public: Cwin(char i, char *text){......} //constructor Cwin(){......} //default constructor, id ='D' , *tit

我试图理解复制构造函数和运算符。我读了一些代码,但就是不明白

这里是主要功能

我从类Cwin中提取最重要的代码

输出:

我能理解是什么引起的。但我无法理解下面的解决方案

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


 };
输出:

为什么要更改它的strcpy title,win.title;进入strcpy this->title,win.title;有很大的不同吗?

说明: 在成员函数内部,您可以编写此->标题或标题。两者都是等价的,除非您有一个名为title的局部函数变量,该变量的名称将隐藏该成员

区别在于您添加了一个赋值运算符

在没有赋值运算符的情况下,语句win1=win2通过逐个成员复制来继续。在这种情况下,指针将按原样复制,以便复制后,两个对象都将指向同一个指向的字符*:

然后设置_数据时,字符串由win1复制到char*指针,但win2指向相同的值

第二个代码段更好地处理了复制:指向的字符串的内容被复制/复制到另一个对象指向的char*。因此,win1和win2将继续使用两个不同的字符串

备注/意见 使用strcpy时,不检查目标是否足够长以包含复制的字符串。这可能会导致缓冲区溢出、内存损坏和许多其他可怕的事情

我强烈建议您尽可能使用std::string而不是char*:默认副本会得到很好的管理,您不必关心内存分配/释放、长度

如果Cwin没有显式地实现它自己的赋值运算符,而您的第一个示例没有,编译器将生成一个默认实现,它只是将成员值按原样从一个对象复制到另一个对象。这就是为什么您的输出对这两个对象都说了同样的话-使用默认assignment操作符实现的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;
  }
};

每当您必须在构造函数中手动分配内存并在析构函数中释放内存时,很可能还需要在复制构造函数和赋值运算符中复制该数据。读一读关于这个问题的书。在这种情况下,CWin需要遵循三的规则才能正确地使用char*,但在使用std::string时可以遵循零的规则。只要有可能,您应该始终努力编写遵循零规则的代码,这会使事情更易于管理。

C++类有一个默认赋值运算符,当您将该类的一个成员赋值给另一个成员时,除非您重载它,否则它会执行一个赋值运算符。除非在初始化时使用复制构造函数

现在,在第一部分的代码中,赋值运算符没有重载,因此它所做的是将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();
}
但在第二部分中,您重载了赋值运算符,它没有复制指针,而是复制指针指向的字符串。main现在将按如下方式运行:

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();
}
因此,给出了结果。
您还必须查看并遵循本文中给出的建议。

从strcpy title,win.title更改为strcpy this->title,win.title不会有任何区别,除非函数中有一个名为title的变量。它写了一个操作符重载=,这会导致这个结果吗?@CodaChang我不明白你在问什么,你定义了一个赋值操作符,但它的行为并不是你想要的?你期望的是什么行为?您的复制构造函数似乎正在复制到未初始化的指针标题,这是未定义的行为。请编辑问题以准确指出问题所在,并如本杰明所说,添加MCVE。是的,可能。因为如果不这样做,默认赋值操作符只复制指针,这意味着对象将指向相同的数据,更改其中一个指向的字符串的内容也将更改另一个。我感觉你的代码有更严重的问题。但是因为你把它缩略得太厉害了,我不知道你真正的代码是什么,什么只是什么的简写
你真的做得很好。这就是为什么我要一个thx来征求你的意见,我理解这个问题。我会更新我的问题。我还使用std::wstring而不是常规字符串来进行promot。人们往往低估了并非所有人都用英语交流这一事实。最好从一开始就做好准备。@DavidHaim:wchar\u t不能跨平台移植,这会影响std::wstring。如果您需要支持多个平台,可以使用std::u16string来表示UTF-16字符串,也可以使用std::string来表示UTF-8字符串。谢谢,这是一个很好的解释和建议。事实上,我遇到了你指出的错误,我通过编辑char-to-wstring修复了这个错误。谢谢你的解释,我阅读了这个答案和你的描述,现在我理解了这个问题。你帮了大忙:谢谢,所以我需要记住这个概念。正如我看到的指针,我需要实现它的复制构造函数。你帮了很大的忙。请仔细阅读。
#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;
  }
};
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();
}