简体   繁体   English

为什么不只有一个?复制构造函数和赋值运算符

[英]Why not only one? Copy constructor and assignment operator

I understand which is invoked in what situation... 我明白在什么情况下调用哪个...

Sample a;
Sample b = a; //calls copy constructor
Sample c;
c = a;        //calls assignment operator

My question is Why these 2 different things exist at all? 我的问题是为什么存在这两种不同的东西? Why can't only one of the two take care of both situations? 为什么不能只有两个中的一个照顾这两种情况?

No, they are different. 不,他们是不同的。

Copy constructor is used for constructing a new object (from another object). 复制构造函数用于构造新对象(来自另一个对象)。 In this case you just need to initialize the members. 在这种情况下,您只需要初始化成员。

Assignment operator is used for an existing object (you may have constructed it by default constructor etc), and then assign it by another object. 赋值运算符用于现有对象(您可能已通过默认构造函数等构造它),然后由另一个对象分配它。 In this case you need to re-initialize members, sometimes means destroying and initializing them again. 在这种情况下,您需要重新初始化成员,有时意味着再次销毁和初始化它们。

Even so, the functionality of them are so similar, so you can share their implementation usually. 即便如此,它们的功能也非常相似,所以您通常可以共享它们的实现。 Such as: What is the copy-and-swap idiom? 如: 什么是复制和交换成语?

The copy constructor is invoked on creation, that means you don't have to take care of old resources in your object. 复制构造函数在创建时调用,这意味着您不必处理对象中的旧资源。 The assignment operator on the other hand has to free the old resources. 另一方面,赋值运算符必须释放旧资源。 Besides they have different semantical meaning. 除此之外,它们具有不同的语义。

Suppose the String class: 假设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;
    }
};

(live example) (实例)

Appearently, copy constructor and copy assignment operator does different things. 显然, 复制构造函数复制赋值运算符可以做不同的事情。 Look at the String::String(const String &other) . 查看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);

It initializes its object. 初始化它的对象。 Set m_len , m_alloced , and m_str , and strcpy the string. 设置m_lenm_allocedm_str ,并strcpy字符串。

However, the String &String::operator =(const String &rhs) does - 但是, 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;

modify its object. 修改其对象。 Allocate more memory if required, and re set m_len and re copy the string. 如果需要,分配更多内存,并重新设置m_len重新复制字符串。

Copy constructor is called when the object is created, and does initialize its object. 在创建对象时调用复制构造函数 ,并初始化其对象。

But copy assignment operator is called after the object is created, and does modify its object. 但是创建对象之后调用复制赋值运算符,并且会修改其对象。


For example, look at this code. 例如,看看这段代码。

String str1 = "asdf";
String str2 = str1;
String str3 = "12";
str3 = str1;

(it's also in the live example) (它也在实例中)

str1 is initialized by String::String(const char *) . str1String::String(const char *)初始化。 As you know, it'll contain "asdf" . 如你所知,它将包含"asdf"

str2 is initialized by copy constructor , String::String(const String &other) . str2复制构造函数初始化, String::String(const String &other) By the copy constructor, str2 will contain the same content. 通过复制构造函数, str2将包含相同的内容。

str3 is initialized by String::String(const char *) , like str1 . str3String::String(const char *)初始化,与str1类似。 However, in line 4, it is modified by copy assignment operator . 但是,在第4行中,它由复制赋值运算符 修改 So, str3 contains "12" at first, but its content will be modified into "asdf" , by copy assignment operator. 因此, str3包含"12" ,但其内容将通过复制赋值运算符修改为"asdf"

Well, you could technically get away with just having a copy-constructor and a destructor, but that would require that every time you wanted to do an assignment you would have to destroy the object and then reconstruct it. 好吧,你可以在技术上逃脱只有一个复制构造函数和析构函数,但这需要每次你想要做一个赋值,你必须销毁对象,然后重新构建它。 This would become HIGHLY inefficient in the vast majority of use cases. 在绝大多数用例中,这将变得非常低效。

We cannot just have an operator= either, as you have no idea what the left-hand-side of the operator will be if it has not yet been constructed. 我们不能只有一个operator= ,因为你还不知道如果尚未构造运算符的左侧是什么。 It's members would probably contain garbage data and "shit would hit the fan". 它的成员可能会包含垃圾数据,“狗屎会袭击粉丝”。

Thus, you can think of the need for both an operator= and a copy-constructor as an optimization. 因此,您可以将operator=和copy-constructor都视为优化。


That being said there are a few design-patterns that can reduce the amount of code you have to write. 话虽如此,有一些设计模式可以减少你必须编写的代码量。 For example, assuming you have a copy-constructor, a destructor and a swap-function implemented for your class. 例如,假设您有一个复制构造函数,一个析构函数和一个为您的类实现的交换函数。 You could implement operator= using a copy-swap operation: 您可以使用copy-swap操作实现operator=

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

Whilst this operator has strong exception safety, it is worth to notice that it may be much less efficient than reusing preallocated resources (which a more advanced operator= might do). 虽然此运算符具有强大的异常安全性,但值得注意的是,它可能比重用预分配资源(更高级的operator=可能会这样做)效率低得多。

Basically, they are used in different situations, many correct answer, i just wanna add something to clear out where the copy constructor is also used: 基本上,它们用于不同的情况,许多正确的答案,我只是想添加一些东西来清除复制构造函数的使用位置:

For example: 例如:

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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM