C++ 为什么不止一个?复制构造函数和赋值运算符

C++ 为什么不止一个?复制构造函数和赋值运算符,c++,copy-constructor,copy-assignment,C++,Copy Constructor,Copy Assignment,我知道在什么情况下调用哪个 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

我的问题是为什么这两种不同的东西存在?为什么这两种情况不能只有其中一种处理?

复制构造函数在创建时被调用,这意味着您不必处理对象中的旧资源。另一方面,赋值操作符必须释放旧资源。此外,它们有不同的语义意义

不,他们是不同的

复制构造函数用于(从另一个对象)构造新对象。在这种情况下,您只需要初始化成员

赋值运算符用于现有对象(您可能已经通过默认构造函数等构造了它),然后由另一个对象赋值。在这种情况下,您需要重新初始化成员,有时意味着再次销毁和初始化它们


尽管如此,它们的功能非常相似,因此您通常可以共享它们的实现。例如:

假设
字符串
类:

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;
    }
};
它初始化它的对象。设置
m_len
m_alloced
、和
m_str
,并压缩字符串

但是,
String&String::operator=(conststring&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;

str1
String::String(const char*)
初始化。如您所知,它将包含
“asdf”

str2
由复制构造函数
String::String(conststring和other)
初始化。通过复制构造函数,
str2
将包含相同的内容

str3
String::String(const char*)
初始化,就像
str1
一样。但是,在第4行中,它由复制赋值运算符修改。因此,
str3
最初包含
“12”
,但它的内容将被复制赋值操作符修改为
“asdf”

好吧,从技术上讲,你可以不使用复制构造函数和析构函数,但这就要求每次你想做一个任务时,你必须先摧毁这个对象,然后再重建它。在绝大多数用例中,这将变得非常低效

我们也不能只拥有一个
操作符=
,因为如果还没有构造它,您不知道操作符的左侧是什么。它的成员可能会包含垃圾数据和“狗屎会砸到风扇”

因此,您可以将对
操作符=
和复制构造函数的需求看作是一种优化


也就是说,有一些设计模式可以减少必须编写的代码量。例如,假设您为类实现了复制构造函数、析构函数和交换函数。您可以使用复制交换操作实现
operator=

MyClass & operator=( MyClass rhs ) { // notice the call by value, this will 
                                     // implicitly call the copy-constructor.
    swap(*this, rhs);
    return *this;
}

虽然此操作符具有很强的异常安全性,但值得注意的是,它可能比重用预分配的资源(更高级的
操作符=
可能会做到这一点)效率低得多

基本上,它们在不同的情况下使用,许多答案都是正确的,我只想补充一点,以澄清复制构造函数也在哪里使用:

例如:

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
}

因为它们是不同的东西。但是,您可以在赋值的实现中利用复制(请参见复制和交换习惯用法)。考虑一个没有默认构造函数或其默认构造函数具有不良副作用的类。您必须有一个副本构造函数。但是你怎么做呢仅使用一个复制构造函数?
MyClass & operator=( MyClass rhs ) { // notice the call by value, this will 
                                     // implicitly call the copy-constructor.
    swap(*this, rhs);
    return *this;
}
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
}