c++ 对象所有权的手记

1.先理解下c++为什么会需要move?

 ---- 变量,所有权,以及性能优化。

2.变量

从实用的角度,变量可以分为两类:

  1. 没有指针或引用的变量,可以称为trivial type。  (c++11,平凡可复制的概念用的也是trivial这个词)
  2. 有指针或引用的变量,可以称handle type。

简化理解,trivial type数据,没有指针、引用直接放在栈上的,无须优化性能。

handle type通常有两部分数据:第一部分可以放在栈上,第二部分在堆里

栈上的数据——就叫做handle,用来控制(handle)堆上的数据。从这个角度看栈上的数据是堆数据的所有者(owner) )。

c++ 语法move一般针对的是handle type的变量。

为什么Python,JavaScript,Java等等都没有move这个概念呢?

因为在这些有GC的语言里面,它们处理handle type的时候,赋值既具有"move"的性质,又具有copy的性质——实际是将引用复制给另外一个变量。

比如下面JavaScript代码

let a = { name: "good"};
let b = a;
b.name = "Bad";
// a,b的名字都变成了bad.

这里变量b与a引用着同样的对象,它们就是一个引用而已。

b = a就是将这个名字给了b变量,它们指代的是同一个东西。

当我们通过b修改数据,此时a引用着的对象自然而然就已经修改了。

显然,此时无所谓move动作,因为b跟a指代同样的对象,这是很直观的想法。

更准确的说法是copy 栈上的名字,根本没有发生任何所有权的转移概念。

我们看下 C++里面,类似的b = a,默认的是拷贝复制,而不是引用。

void test()
{
	auto a = vector<string>{ "str1", "str2", "str3" };
		auto b = a;
		b[0] = "b string";
		for (auto v : a) {
			cout << v << ", ";
		}
		cout << endl;
		for (auto v : b) {
			cout << v << ", ";
		}
		cout << endl;
}

输出:

str1, str2, str3,
b string, str2, str3,

对比js的引用a、b,都在栈上,天然的指向同一个堆空间,只是别名。

而c++的变量a、b,都在栈上,指向了不同的堆空间,b=a,是复制了一份给到b指向的空间。

(像java,JS等有GC的语言为了更好地管理内存,基本不做堆栈的区分——不用关心是在栈上还是堆里。GC帮忙管理着内存,这是这些语言没有move概念的原因)

上面JS例子只拷贝了栈上面的引用(它们无须操作堆复制),而C++既拷贝了栈那部分数据也拷贝了堆里面的内容

如果,c++想实现所有权的转换呢,因此,C++11 引入了move的概念,我们只拷贝栈上的数据的时候,并引用到已有的堆数据(不另外分配堆内存并拷贝堆数据,提高性能)。

3.value categories也做了调整,从而规定哪些value可以被move

(左值、右值、将亡值)

  1. prvalue - 纯右值 。就是用于计算的或者用于初始化对象的。(pure)
  2. xvalue -  将亡值。就是临时变量。它是快要被销毁的值。(expiring)
  3. lvalue -  左值。在内存中具有位置的值。所有具名变量都是左值 (left)

xvalue可以被直接move。

prvalue被move的时候,可以理解生成了一个临时变量xvalue,并move了这个临时变量。

lvalue,则需要使用std::move将lvalue变成xvalue,并move了这个xvalue。

4.c++中的move

        std::string a = "stringaaa";
        auto b = std::move(a);
        cout << b << endl;
原来变量a指代的值的所有权转移给了b,变量a被置成“空”了

move的场景例子。

  1. 接管资源
void My::take(Book && iBook) 
{
  mBook = std::move(iBook); //将没人要的iBook,拿过来据为己有
}

2. 转移所有权

auto thread = std::thread([]{});
std::vector<std::thread> lThreadPool;
lThreadPool.push_back(std::move(thread)); //现在thread pool来掌控着thread

3. 避免拷贝

void f() {
  std::vector v = ...;
  take(std::move(v)); // 直接move进了函数take里面,不用拷贝
}

move是转移对象的所有权,所以我们可以move的是可以转移所有权的对象。

可以移动右值,也可以把左值通过std::move变成右值,从而可以实现移动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值