移动构造和移动赋值

MyString func(const char* p)
	{
		MyString tmp(p);
		tmp.PrintString();
		return tmp;
	}

int main()
{
	MyString s1("newdata");
	s1.PrintString();
	s1 = func("yhpinghello");
	s1.PrintString();
	return 0;
}

首先,想上述代码中的yhpinghello这个字符串常量存储在只读数据区(.rdata)。

这是这个代码的调用图和运行结果

可以发现,这对堆区的骚扰很严重,所以可以进行移动构造和移动赋值。

一、移动构造和移动赋值

移动构造的意思就是让本来s1的资源移动给s2,然后将s1的资源置为空。当然现在我们无法真正的把它移动走,所以move真正的目的是把s1从左值变为右值。 如果我们想要真正的把资源移动走,我们还要加上配套的类型设计。

class MyString
{
private:
	char* ptr;
public:
	MyString(const char* str = nullptr)
	{
		if (str != nullptr)
		{
			int len = strlen(str);
			ptr = new char[len + 1];
			strcpy(ptr, str);
		}
		else
		{
			ptr = new char[1];
			*ptr = '\0';
		}
	}
	~MyString()
	{
		if (ptr != nullptr)
		{
			delete[]ptr;
		}
		ptr = nullptr;
	}
	void PrintString()
	{
		if (ptr != nullptr)
		{
			cout << ptr << endl;
		}
	}
	MyString(const MyString& st)
	{
		if (st.ptr != nullptr)
		{
			int len = strlen(st.ptr);
			ptr = new char[len + 1];//圆括号代表开辟一个空间,方括号代表开辟一组连续空间
			strcpy(ptr, st.ptr);
		}
		else
		{
			ptr = nullptr;
		}
	}
	MyString& operator=(const MyString& it)
	{
		if (this == &it)
			return *this;
		delete[]this->ptr;
		this->ptr = nullptr;
		if (it.ptr != nullptr)
		{
			int len = strlen(it.ptr);
			ptr = new char[len + 1];
			strcpy(ptr, it.ptr);
		}
		return *this;
	}
	MyString(MyString&& st) :ptr(st.ptr)
	{
		st.ptr = nullptr;
		cout << this << "move  creat" << &st << endl;

	}
};
int main()
{
	MyString s1("newdata");
	MyString s2(std::move(s1));
	return 0;
}

这样就可以把s1的资源弄到s2中,并把s1置为空,这就叫做移动构造。 

移动构造必须要满足一些条件:

首先被转移资源的对象必须是右值,如果不是要用move将它变成右值。

还必须有相应的移动构造函数配合,这样才能进行移动构造。

如果被转移资源的对象具有常性,move无法把它转移成右值。

int main()
{
	const MyString s1("newdata");
	//MyString s2(std::move(s1));
	MyString s2((MyString&&)s1);
	return 0;
}

但是可以这样进行强转。

移动赋值也是如此,也要满足必须是右值和有相应的移动赋值函数来解决。

class MyString
{
private:
	char* ptr;
public:
	MyString(const char* str = nullptr)
	{
		if (str != nullptr)
		{
			int len = strlen(str);
			ptr = new char[len + 1];
			strcpy(ptr, str);
		}
		else
		{
			ptr = new char[1];
			*ptr = '\0';
		}
	}
	~MyString()
	{
		if (ptr != nullptr)
		{
			delete[]ptr;
		}
		ptr = nullptr;
	}
	void PrintString()
	{
		if (ptr != nullptr)
		{
			cout << ptr << endl;
		}
	}
	MyString(const MyString& st)
	{
		if (st.ptr != nullptr)
		{
			int len = strlen(st.ptr);
			ptr = new char[len + 1];//圆括号代表开辟一个空间,方括号代表开辟一组连续空间
			strcpy(ptr, st.ptr);
		}
		else
		{
			ptr = nullptr;
		}
	}
	MyString& operator=(const MyString& it)
	{
		if (this == &it)
			return *this;
		delete[]this->ptr;
		this->ptr = nullptr;
		if (it.ptr != nullptr)
		{
			int len = strlen(it.ptr);
			ptr = new char[len + 1];
			strcpy(ptr, it.ptr);
		}
		return *this;
	}
	MyString(MyString&& st) :ptr(st.ptr)
	{
		st.ptr = nullptr;
		cout << this << "move  creat" << &st << endl;

	}
	MyString & operator=(MyString &&st)
	{
		if (this == &st) return *this;
		delete[]ptr;
		ptr = st.ptr;
		st.ptr = nullptr;
		cout << this << "operator =move" << &st << endl;
		return *this;

};
int main()
{
	MyString s1("yhping");
	MyString s2("newdata");
	s1.PrintString();
	s2.PrintString();
	s2 = std::move(s1);
	s1.PrintString();
	s2.PrintString();
	return 0;
}

由此可见,s1的资源被移动赋值给了s2,然后s1的资源为空。 

我们此时在回到堆区骚扰严重的那个问题上:

MyString func(const char* p)
{
	MyString tmp(p);
	tmp.Print();
	return tmp;
}
int main()
{
	MyString s1("newdata");
	s1.Print();
	s1 = func("yhpinghello");
	s1.Print();
	return 0;
}

它的过程图是这样的:

这里其他部分都是正常的,着重解释一下return tmp;部分,这部分是重点。

在return tmp的时候,我们会构建一个亡值对象,这里会调用移动构造函数将亡值对象ptr所指的资源指向tmp对象ptr所指资源,然后将tmp对象ptr所指对象资源置为空。这样当函数栈销毁,将tmp对象析构掉的时候并不会影响堆区的数据。

然后这里在赋值过程中调用的也是移动赋值,原理相同。

对比下来,拥有移动构造和移动赋值可以减少对堆区的骚扰。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值