C++的拷贝构造和移动构造

一、拷贝构造

  如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都没有默认值,则此构造函数是拷贝构造函数。(《C++Primer,第五版》)

class Foo
{
public :
    Foo();
    Foo(const Foo&); //拷贝构造函数
    Foo& operator=(const Foo&); //拷贝赋值运算符
};

类的成员中有指针时,使用深拷贝。

#include <iostream>
using namespace std;

class Foo
{
public:
    Foo()
    {
        pInt = new int;
        *pInt = 1;
    }
    Foo(const Foo&)= default; //拷贝构造函数
    Foo& operator=(const Foo&) = default; //拷贝赋值运算符

    int* pInt;
};

//浅拷贝,foo1和foo2中的pInt指向同一块内存地址
Foo foo1;
Foo foo2(foo1);

//深拷贝
class Cop
{
public:
    Cop()
    {
        pInt = new int;
        *pInt = 2;
    }
    Cop(const Cop& cop)
    {
        pInt = new int;
        *pInt = *(cop.pInt);
    }
    int* pInt;
};
//深拷贝,cop1和cop2中pInt指向不同内存
Cop cop1;
Cop cop2(cop1);

 

二、移动构造

  在某些情况下(函数返回对象引用),对象拷贝后立即就被消耗了。拷贝构造就回造成性能上的浪费,而且深拷贝也会造成浪费。移动构造可以避免这种情况的发生。

  为了支持移动构造,C++11引入了右值引用。

  右值引用:必须绑定到右值的引用,通过&&来获得右值引用,类似左值引用(常规引用),右值应用也是一块内存的别名。

  右值:字面常量、在表达式求值过程中临时创建的对象,这些使用过后就被销毁的资源。不同于左值的持久状态。

  因此,右值引用只能绑定到将要被销毁的对象上,左值引用只能绑定到持久的对象上。

 

//例子来源于  《C++primer 第五版》

int i = 42;
int& r = i;        //正确,i是左值
int&& rr = i;    //错误
int& r2 = i * 42;    //错误,i*42是右值,用完即销毁
const int& r3 = i * 42;    //正确,隐式转换为左值后,r3引用
int&& rr2 = i * 42;    //正确,绑定右值
int&& rr1 = 42;  //正确,字面常量是右值
int&& rr2 = rr1;  //错误,rr1左值

        变量都是左值,因此无法将右值引用绑定到一个右值引用类型的变量上。

   如果要将右值引用绑定到左值上,可以通过move函数来获得左值的右值引用类型。对一个左值调用move函数后,除了对该左值赋值和销毁外,不再使用它。

 

int i = 42;
int&& rr = move(i);

//i和rr引用同一块内存

i = 32;

cout << i << endl;
cout << rr << endl;

  移动构造函数依靠右值引用特性来将来改变内存的管理者,而不同于拷贝构造对内存进行拷贝。

   移动构造:第一个参数是该类类型的一个右值引用,且任何额外的参数都必须有默认实参。使用移动构造函数必须确保销毁移后源对象是无害的(不会重复释放同一块内存)。

Cop(Cop&& cop) noexcept : pInt(cop.pInt)
{
    cout << "this is &&" << endl;
    cop.pInt = NULL;
}
Cop& operator=(Cop&& cop) noexcept
{
   if ( this == &cop ) return *this;
    cout << "this is && =" << endl;
    delete pInt;
    pInt = cop.pInt;
    cop.pInt = NULL;

}


Cop retCop()
{
    Cop cop3;
    *cop3.pInt = 3;
    return cop3;
}

//调用移动赋值运算符,cop3中pInt接管retCop返回对象中的pInt指向内存
Cop cop3 = retCop();

noexcept承诺函数不抛出异常,标准库对这个函数不做额外处理。

 

静态拷贝构造函数(static copy constructor)和移动构造函数(move constructor)是 C++ 中用于对象的复制和移动的特殊成员函数。 拷贝构造函数(copy constructor)用于创建一个新对象,该对象与已经存在的对象具有相同的值。静态拷贝构造函数是指不需要修改成员变量的拷贝构造函数,通常是拷贝成员变量值的过程。 移动构造函数(move constructor)则用于将资源从一个对象转移到另一个对象,通常是在源对象不再被使用时将其资源转移到目标对象,这样可以避免不必要的复制操作,提高性能。移动构造函数通常使用右值引用参数(&&)来接收源对象。 在 C++11 中引入了右值引用和移动语义的概念,允许程序员通过移动语义来提高代码的效率。对于可移动的对象,应该尽可能地使用移动语义来避免不必要的拷贝操作。 如果没有显式定义静态拷贝构造函数移动构造函数,编译器会自动生成默认的拷贝构造函数移动构造函数。但如果类中包含了指针成员变量或资源管理类等需要特殊处理的情况,就需要手动定义这两个函数。 请注意,静态拷贝构造函数移动构造函数是两个不同的概念,静态拷贝构造函数并不具备移动语义。移动构造函数通常会采用右值引用参数,而静态拷贝构造函数则通常采用常量引用参数。 这就是关于静态拷贝构造函数移动构造函数的一些介绍,希望能对你有所帮助。如果有任何疑问,请随时追问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值