繁体   English   中英

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

[英]Why not only one? Copy constructor and 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_lenm_allocedm_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;

修改其对象。 set m_len and copy the string. 如果需要,分配更多内存,并设置m_len复制字符串。

在创建对象时调用复制构造函数 ,并初始化其对象。

但是创建对象之后调用复制赋值运算符,并且会修改其对象。


例如,看看这段代码。

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

(它也在实例中)

str1String::String(const char *)初始化。 如你所知,它将包含"asdf"

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

str3String::String(const char *)初始化,与str1类似。 但是,在第4行中,它由复制赋值运算符 修改 因此, str3包含"12" ,但其内容将通过复制赋值运算符修改为"asdf"

好吧,你可以在技术上逃脱只有一个复制构造函数和析构函数,但这需要每次你想要做一个赋值,你必须销毁对象,然后重新构建它。 在绝大多数用例中,这将变得非常低效。

我们不能只有一个operator= ,因为你还不知道如果尚未构造运算符的左侧是什么。 它的成员可能会包含垃圾数据,“狗屎会袭击粉丝”。

因此,您可以将operator=和copy-constructor都视为优化。


话虽如此,有一些设计模式可以减少你必须编写的代码量。 例如,假设您有一个复制构造函数,一个析构函数和一个为您的类实现的交换函数。 您可以使用copy-swap操作实现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
}

暂无
暂无

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

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