复制构造函数
复制构造函数在之前整理过了
class A{
public:
A(const A& rhs);//复制构造函数
};
复制构造函数常见的问题就是浅拷贝问题,因此会重写复制构造函数改为深拷贝。
因此就出现了一个问题,在很多情况下我们只需要创建一个临时变量,或者只是一个函数调用的返回值;
这时候用复制构造函数就会占用很多空间,如果对象占用的堆空间非常大,那么复制构造函数就要复制一个非常大的堆空间,之后再析构。耗时耗空间。
移动构造函数
- 为了减少不必要的复制,提升性能,c11中加入了移动构造函数。
移动构造函数是什么?先举个例子,你有一本书,你不想看,但我很想看,那么我有哪些方法可以让我能看这本书?有两种做法,一种是你直接把书交给我,另一种是我去买一些稿纸来,然后照着你这本书一字一句抄到稿纸上。
显然,第二种方法很浪费时间,但这正是有些深拷贝构造函数的做法,而移动构造函数便能像第一种做法一样省时,第一种做法在 C++ 中叫做完美转发。
- 什么时候用移动构造函数呢?
如果临时对象即将消亡,并且它里面的资源是需要被再利用的,这个时候我们就可以触发移动构造。
#include<iostream>
using namespace std;
class intNum{
public:
intNum(int x=0):ptr_(new int(x)){
cout<<"constructor"<<endl;
}
intNum(const intNum& n):ptr_(new int(*n.ptr_)){
cout<<"copy constructor"<<endl;
}
intNum(intNum&& n):ptr_(n.ptr_){
n.ptr_=nullptr;
cout<<"move constructor"<<endl;
}
~intNum(){
delete ptr_;
cout<<"destructor"<<endl;
}
int getInt(){return *ptr_;}
private:
int *ptr_;
};
intNum getNum(){
intNum a;
cout<<"---getNum---"<<endl;
return a;
}
int main(){
intNum a(intNum(5));//很奇怪的是这种情况不会使用移动构造函数intNum a(std::move(intNum(5)));这样才行
cout<<a.getInt()<<endl;
intNum b=std::move(a);
//cout<<getNum().getInt()<<endl;这个也不会使用移动构造函数。
return 0;
}
#include<iostream>
#include<string.h>
#include<vector>
using namespace std;
class myString{
private:
char* data_;
size_t len_;
void init_data_(const char *s){
data_=new char[len_+1];
memcpy(data_,s,len_);
data_[len_]='\0';
}
public:
myString(){
data_=nullptr;
len_=0;
}
myString(const char *p){
len_=strlen(p);
init_data_(p);
cout<<"contructor"<<endl;
}
myString(myString&& s){
cout<<"move constructor"<<endl;
len_=s.len_;
data_=s.data_;
s.data_=nullptr;
s.len_=0;
}
myString(const myString& s){
len_=s.len_;
init_data_(s.data_);
cout<<"copy constructor"<<endl;
}
myString& operator=(const myString& s){
if(this!=&s){
len_=s.len_;
init_data_(s.data_);
}
cout<<"copy assignment"<<endl;
return *this;
}
myString& operator=(myString&& s){
cout<<"move assignment"<<endl;
if(this!=&s){
len_=s.len_;
data_=s.data_;
s.data_=nullptr;
s.len_=0;
}
return *this;
}
virtual ~myString(){
if(data_)free(data_);
}
char* getdata(){
return data_;
}
};
myString getstring(){
myString a("hello_getstring");
return a;
}
int main() {
myString c(std::move(getstring()));
myString a(myString("hello"));
myString b;
b=a;
std::vector<myString> vec;
vec.push_back(myString("World"));
return 0;
}
- 不知道为啥只有用vector和std::move的时候才能使用到移动构造函数和移动赋值函数。
理论上说,上文提及到常量左值引用也可以接受右值,而右值引用也可以接受右值,那一个右值是否有可能会套入一个参数类型为常量左值引用的函数呢?答案是不会,一个右值要套入函数时,会优先选择套入参数类型为右值引用的函数。
左值和右值的定义
C++( 包括 C) 中所有的表达式和变量要么是左值,要么是右值。通俗的左值的定义就是非临时对象,那些可以在多条语句中使用的对象。所有的变量都满足这个定义,在多条代码中都可以使用,都是左值。右值是指临时的对象,它们只在当前的语句中有效。请看下列示例 :
- 简单赋值语句
int i=0;
i是左值,0是临时的数值就是右值。立即数都是右值。
- 右值也可以出现在赋值表达式的左边,但是不能作为赋值的对象,因为右值只在当前语句有效,赋值没有意义。
如:((i>0) ? i : j) = 1;