C++ 复制构造函数和运算符
C++ Copy constructor and Operator
我试图理解 Copy constructor
和 Operator
。我读了一些代码,但我就是不明白。
这里是主要函数
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->title
或title
。两者是等效的,除非您有一个名为 title
的局部函数变量,然后该名称将隐藏该成员。
不同之处在于您添加了赋值运算符。
如果没有赋值运算符,您的语句 win1=win2 通过逐个成员复制来进行。在这种情况下,一个指针被原样复制,所以在复制之后,两个对象将指向同一个指针 char*
:
当您 set_data()
时,字符串被 win1
复制到 char*
指针,但 win2
指向相同的值。
你的第二个代码片段,更好地处理复制:指向的字符串的内容是 copied/duplicated 到 char*
另一个对象指向的。 win1
和 win2
将因此继续使用 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();
}
因此,给出的结果。
您还必须查看并遵循此 .
中给出的建议
我试图理解 Copy constructor
和 Operator
。我读了一些代码,但我就是不明白。
这里是主要函数
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->title
或title
。两者是等效的,除非您有一个名为 title
的局部函数变量,然后该名称将隐藏该成员。
不同之处在于您添加了赋值运算符。
如果没有赋值运算符,您的语句 win1=win2 通过逐个成员复制来进行。在这种情况下,一个指针被原样复制,所以在复制之后,两个对象将指向同一个指针 char*
:
当您 set_data()
时,字符串被 win1
复制到 char*
指针,但 win2
指向相同的值。
你的第二个代码片段,更好地处理复制:指向的字符串的内容是 copied/duplicated 到 char*
另一个对象指向的。 win1
和 win2
将因此继续使用 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();
}
因此,给出的结果。
您还必须查看并遵循此