拷贝构造与拷贝赋值
调用拷贝构造函数主要有以下场景:
1:对象作为函数的参数,以值传递的方式传给函数。
2:对象作为函数的返回值,以值的方式从函数返回。
3:使用一个对象给另一个对象初始化。
构造 就是产生了新的对象;
赋值 是 已有对象改变他的值;
#include<iostream>
class Person1 {
private:
std::string name;
int age;
public:
Person1(){}
Person1(const char* name, int age) { this->name = name; this->age = age; }
~Person1(){}
//Person1(const Person1& p) { this->name = p.name; this->age = p.age; std::cout << "构造" << std::endl; }//拷贝构造函数
//Person1& operator=(const Person1& p) { this->name = p.name; this->age = p.age; std::cout << "赋值"; return *this; }//拷贝赋值函数
void view() { std::cout << age; }
};
int main() {
Person1 xm("xm", 18);
Person1 lm = xm;//调用拷贝构造函数
Person1 mn;
mn = xm;//调用赋值构造函数
lm.view();
}
注释的那两行去掉试一下就是这两个函数 ;
右值引用和移动语义
拷贝,顾名思义,产生新的对象,而原有对象不变。这增加了多余的操作;
移动 就是 直接把某个对象的所有权 直接转让给某个调用。不产生临时对象。比如在计算机中移动文件的场景,实际文件还停留在原来位置,只是修改了记录。
c++11新增了右值引用和移动语义(为了移动构造做铺垫)
对于大多数程序员来说,右值引用带来的好处并不是让我们能够编写使用右值引用的代码,而是弄够利用右值引用来实现移动语义的库代码。例如,STL类都有我们提到的这四个函数。
介绍一下什么是右值与左值
int x; //左值
int y = x+z;(x+z)为右值
右值引用 可关联到右值 比如
int y = x+z;y关联的是当时计算的x+y的结果,即使以后修改了x,z的值,也不会影响y。
将右值关联到右值引用将导致该右值被储存到特定的位置,且可以获取该位置的地址。也就是说,int &&r1 = 13来说 我们虽然不可以将&作用在13上,但是可以用在r1上。通过将数据与特定的地址关联,使得可以通过右值引用来访问该数据。(这就对应了上述让出所有权,实现移动)
实现移动语义是引入右值引用的主要目的。
移动构造和移动赋值
对于移动构造函数的原型
Person1( Person1 && p);
这时 我们的参数传递为一个右值
#include<iostream>
class Person1 {
private:
std::string name;
int age;
public:
Person1(){}
Person1(const char* name, int age) { this->name = name; this->age = age; }
~Person1(){}
Person1(const Person1& p) { this->name = p.name; this->age = p.age; std::cout << "构造" << std::endl; }//拷贝构造函数
Person1& operator=(const Person1& p) { this->name = p.name; this->age = p.age; std::cout << "赋值"<<std::endl; return *this; }//拷贝赋值函数
int view() { std::cout << age; return age; }
Person1(Person1 && p) noexcept { this->name = p.name; this->age = p.age; p.age = 0; p.name = ""; std::cout << "移动构造" << std::endl; }
Person1 operator = (Person1&& p) noexcept { if (this == &p)return *this; this->age = p.age; p.age = 0; p.name = ""; std::cout << "移动赋值" << std::endl; return *this; }
Person1 operator +(Person1 p) noexcept { this->age = this->age + p.age; std::cout << "+"; return *this; }
};
int main() {
Person1 xa("xa", 11);
Person1 xb("xb", 22);
Person1 xc (std::move(xa + xb));
xc.view();
}
受版本影响 加了 std::move()这是强制语义转化,正常不用加。这个函数是将左值强制转化为右值。受左右值的影响,编译器才能区分何时用复制构造,何时用移动构造。
另外,我举得这个例子其实不是太好。如果移动构造过程中,4原对象有指针。那么,移动构造函数中要把指针指向nullptr。并在析构函数中释放掉它。
对于移动赋值,也就很好理解了。
例子并不好,希望大家自己改一改。