我明白在什么情况下调用哪个...
Sample a;
Sample b = a; //calls copy constructor
Sample c;
c = a; //calls assignment operator
我的问题是为什么这两种不同的东西存在?为什么不能只有两者中的一个同时处理这两种情况?
我明白在什么情况下调用哪个...
Sample a;
Sample b = a; //calls copy constructor
Sample c;
c = a; //calls assignment operator
我的问题是为什么这两种不同的东西存在?为什么不能只有两者中的一个同时处理这两种情况?
不,它们是不同的。
复制构造函数用于构造一个新对象(从另一个对象)。在这种情况下,您只需要初始化成员。
赋值运算符用于现有对象(您可能已经通过默认构造函数等构造了它),然后由另一个对象分配它。在这种情况下,您需要重新初始化成员,有时意味着再次销毁和初始化它们。
即便如此,它们的功能是如此相似,因此您通常可以共享它们的实现。如:什么是复制和交换成语?
创建时调用复制构造函数,这意味着您不必处理对象中的旧资源。另一方面,赋值运算符必须释放旧资源。此外,它们具有不同的语义含义。
假设String
类:
class String
{
char *m_str;
size_t m_len, m_alloced;
public:
String(const char *str = "")
{
m_len = strlen(str);
m_alloced = m_len + 1;
m_str = (char *)malloc(m_alloced);
strcpy(m_str, str);
}
String(const String &other)
{
m_len = other.m_len;
m_alloced = m_len + 1;
m_str = (char *)malloc(m_alloced);
strcpy(m_str, other.m_str);
}
String &operator =(const String &rhs)
{
if (m_alloced < rhs.m_len + 1)
{
m_alloced = rhs.m_len + 1;
m_str = (char *)realloc(m_str, m_alloced);
}
m_len = rhs.m_len;
strcpy(m_str, rhs.m_str);
return *this;
}
const char *get() const
{
return m_str;
}
};
显然,复制构造函数和复制赋值运算符做了不同的事情。看看String::String(const String &other)
。
m_len = other.m_len;
m_alloced = m_len + 1;
m_str = (char *)malloc(m_alloced);
strcpy(m_str, other.m_str);
它初始化它的对象。设置m_len
, m_alloced
, 和m_str
, 然后 strcpy 字符串。
但是,String &String::operator =(const String &rhs)
确实 -
if (m_alloced < rhs.m_len + 1)
{
m_alloced = rhs.m_len + 1;
m_str = (char *)realloc(m_str, m_alloced);
}
m_len = rhs.m_len;
strcpy(m_str, rhs.m_str);
return *this;
修改其对象。如果需要,分配更多内存,然后重新设置m_len
并重新复制字符串。
创建对象时调用复制构造函数,并初始化其对象。
但是复制赋值运算符是在对象创建后调用的,并且确实修改了它的对象。
例如,看看这段代码。
String str1 = "asdf";
String str2 = str1;
String str3 = "12";
str3 = str1;
str1
由 初始化String::String(const char *)
。如您所知,它将包含"asdf"
.
str2
由复制构造函数初始化,String::String(const String &other)
. 通过复制构造函数,str2
将包含相同的内容。
str3
由 初始化String::String(const char *)
,如str1
。但是,在第 4 行,它被复制赋值运算符修改。所以,一开始是contains ,但它的内容会被复制赋值操作符修改为 ,。str3
"12"
"asdf"
好吧,从技术上讲,你可以只使用一个复制构造函数和一个析构函数,但这将要求每次你想做一个赋值时,你都必须销毁对象然后重建它。在绝大多数用例中,这将变得非常低效。
我们不能只拥有一个operator=
,因为您不知道如果尚未构建运算符的左侧将是什么。它的成员可能包含垃圾数据和“狗屎会击中粉丝”。
因此,您可以将对 anoperator=
和 copy-constructor 的需求视为一种优化。
话虽如此,有一些设计模式可以减少您必须编写的代码量。例如,假设您为您的类实现了一个复制构造函数、一个析构函数和一个交换函数。您可以operator=
使用复制交换操作来实现:
MyClass & operator=( MyClass rhs ) { // notice the call by value, this will
// implicitly call the copy-constructor.
swap(*this, rhs);
return *this;
}
operator=
尽管此运算符具有很强的异常安全性,但值得注意的是,它可能比重用预先分配的资源(更高级的可能会这样做)效率低得多。
基本上,它们在不同的情况下使用,许多正确答案,我只是想添加一些东西来清除复制构造函数的使用位置:
例如:
void test(Fraction in){
//Do something here
}
Fraction test2(){
Fraction a(1, 2);
//Do something here
return a; // construct a return value by copy a
}
int main()
{
Fraction a(2, 3);
Fraction b(a); //construct b by copy a
Fraction c=a; //construct c by copy a
test(a); //construct in by copy a
}