C++中的拷贝构造,赋值和移动构造

本文通过一个Person类的实例详细介绍了C++中的拷贝构造函数、移动构造函数、赋值运算符及移动赋值运算符的概念与应用场景。特别强调了在不同条件下这些函数的调用时机。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在说明这几个名词时,我们需要定义一个测试类Person,Person类的测试环境为vs2013

一般情况下,在对象声明时用拷贝构造函数对对象进行初始化(在编译器进行优化的时候也会使用移动构造函\
数(在有临时对象产生的情况下),这样效率会高一些,如避免深度拷贝之类的操作),一旦初始化之后,在\
进行=运算将会使用赋值运算(在有临时对象产生的情况下将会调用移动赋值运算符),认真的分析一下下面的\
示例程序吧!

Person类的声明,其中定义了两个数据成员:name (string), id (int), 并且包含了这两个数据域的\
setter方法,同时也实现了拷贝构造函数,赋值运算符,移动构造函数,移动赋值预算符(不过没有用到),\
还有一个打印累的数据成员的信息的函数。
//Person.h

#pragma once
#include <string>
using std::string;

class Person
{
    string name;
    int id;
public:
    Person() = default;
    Person(int id, string name);
    ~Person();
    //拷贝构造函数
    Person(const Person& person);
    //赋值
    Person& operator=(const Person& person);
    //移动构造函数
    Person(Person&& person);
    //移动赋值运算符
    Person& operator=(Person&& person);

    Person copyPerson();

    void setId(int id){ this->id = id; }
    void setName(string Name){ this->name = Name; }

    void desc();
};

Person类的方法实现

//Person.cpp

#include "stdafx.h"
#include "Person.h"

using namespace std;


Person::Person(int id, string name):
                id(id), name(name)
{

}

Person::~Person()
{

}

//拷贝构造函数
Person::Person(const Person& person){
    cout << "copy constructor!" << endl;
    this->id = person.id;
    this->name = person.name;
}
//赋值
Person& Person::operator=(const Person& person){
    cout << "operator= " << endl;
    //防止自身对自身赋值,这里可能会存在潜在的bug,如果这里我们分配内存来存储数据,那么我们
    //在赋值运算符中药释放this指针中数据指针成员指向的堆空间,如果这里我们不进行自赋值检查将会
    //导致自身的数据指针成员指向的内存被释放结果可能就不是我们所预期的那样子了。
    if (this == &person){
        return *this;
    }

    this->id = person.id;
    this->name = person.name;

    return *this;
}
//移动构造函数
Person::Person(Person&& person){
    cout << "move constructor!" << endl;
    this->id = person.id;
    this->name = person.name;
    //把移动操作的源对象简单的置为所谓空值(根据自己的应用场景决定)
    person.id = -1;
    person.name = "";
}
//移动赋值运算符
Person& Person::operator=(Person&& person){
    cout << "move operator=" << endl;
    //这里防止自移动,下面要把移动源对象的数据域简单的置为空值,
    //如果源对象就是本身那么最后移动后自己的值被清空了。
    if (this == &person){
        return *this;
    }
    this->id = person.id;
    this->name = person.name;

    person.id = -1;
    person.name = "";

    return *this;
}

void Person::desc(){
    cout << "id:" << id << endl;
    cout << "name:" << name << endl;
    cout << "-------------------------------------------" << endl;
}

Person Person::copyPerson(){
    Person p(this->id, name);

    return p;
}

Person的测试时也就是为了测试移动构造函数,拷贝构造函数,以及赋值在什么时候回发生调用。

//main.cpp

#include "stdafx.h"
#include "Person.h"


int _tmain(int argc, _TCHAR* argv[])
{
    Person p1;
    Person p2;

    p1.setId(1); p1.setName("lucy");
    p2.setId(2); p2.setName("Tom");

    p2 = p1; //p2之前已经被初始化过
    Person p3 = p2; //p3未初始化过,这里通过p2来初始化p3,调用拷贝构造函数
    Person p4(p2); //p4未初始化过,同时这里根据传递的参数也可以判断出调用拷贝构造函数
    Person p5 = p2.copyPerson();//调用移动构造函数
    p5 = p2.copyPerson();//调用移动赋值运算符

    system("pause");
    return 0;
}

程序的运行结果如下图:
程序的运行结果如下图

通过分析我们可以得出以下结论:

  1. 在新对象初始化时调用构造函数,如果在这个初始化过程中有临时对象的产生,编译器将会进行优化,调用移动构造函数。
  2. 在对象初始化以后进行的赋值操作,如果赋值过程中有临时对象产生将会调用移动赋值运算。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值