Go语言 格朗语中的移动语义

xv8emn3q  于 2023-03-10  发布在  Go
关注(0)|答案(4)|浏览(146)

这来自Bjarne Stroustrup的《C++编程语言》,第四版3.3.2。
我们并不是真的想要一份拷贝我们只想得到函数的结果我们想要移动一个Vector而不是复制它。幸运的是,我们可以说明这个意图:

class Vector {
     // ...

     Vector(const Vector& a);          // copy constructor
     Vector& operator=(const Vector& a);     // copy assignment

     Vector(Vector&& a);               // move constructor
     Vector& operator=(Vector&& a);          // move assignment
};

**给定该定义,编译器将选择move构造函数来实现从函数传递返回值。**这意味着r=x+y+z将不涉及Vectors的复制。相反,Vectors只是被移动。通常,Vector的move构造函数很容易定义...

我知道Golang支持使用Go风格指针的传统传值和传引用。
Go语言是否像C++11那样支持“移动语义”,如Stroustrup所描述的那样,以避免无用的来回复制?如果是,这是自动的,还是需要我们在代码中做些什么才能做到。

  • 注意:一些答案已经张贴-我必须消化他们一点,所以我还没有接受一个-谢谢。*
xkrw2x1b

xkrw2x1b1#

细分如下:

  1. Go语言中的一切都是按值传递的。
    1.但是有五个内置的“引用类型”,它们也是通过值传递的,但是它们在内部保存对单独维护的数据结构的引用:Map、切片、通道、字符串和函数值(没有办法改变后两个引用的数据)。
    你自己的答案@Vector是不正确的,因为Go语言中的 nothing 是通过引用传递的,相反,存在具有 reference语义的类型。 它们的值仍然是通过值传递的(原文如此!)
    你的困惑可能源于这样一个事实,即你的大脑目前被C++、Java等所拖累,而这些事情在Go语言中大多是“像在C语言中一样”完成的。
    以数组和切片为例,在Go语言中,数组是通过值传递的,而切片是一个包含指针的压缩结构体(指向基础数组)和两个平台大小的整数(切片的长度和容量),并且它是被复制的 this 结构的值-一个指针和两个整数-当它被赋值或返回时等等。如果你复制一个“裸”数组,它将被逐字复制-包括它的所有元素。
    这同样适用于通道和贴图,你可以把定义通道和贴图的类型想象成这样的声明:
type Map struct {
   impl *mapImplementation
}

type Slice struct {
   impl *sliceImplementation
}

(By顺便说一下,如果您了解C++,您应该知道一些C++代码使用这种技巧来降低头文件中细节的暴露程度。)
所以当你以后

m := make(map[int]string)

你可以把它想象成Map类型的m,当你以后这么做的时候

x := m

m的值被复制,但是它只包含一个指针,所以xm现在引用相同的底层数据结构。m是通过引用复制的吗(“移动语义”)?当然不是!类型map、slice和channel的值有引用语义吗?是的!
请注意,这五种类型根本不特殊:通过在自定义类型中嵌入指向某个复杂数据结构的指针来实现自定义类型是一种相当常见的模式。
换句话说,Go语言允许程序员决定他们的类型需要什么样的语义,而且Go语言碰巧有五个内置类型已经有了引用语义(而所有其他的内置类型都有值语义)。选择一种语义而不是另一种语义并不影响以任何方式通过值复制所有内容的规则。例如,在Go语言中,拥有指向任何类型的值的指针都是可以的。并赋予它们(只要它们具有兼容的类型)-这些 * 指针 * 将按值复制。
从另一个Angular 来看,很多Go语言包(标准和第三方)更喜欢使用指向(复数)值。例如os.Open()(打开文件系统上的一个文件)返回*os.File类型的值。也就是说,它返回一个指针,并 * 期望 * 调用代码传递这个指针。当然,Go语言的作者可能已经声明os.File是包含单个指针的struct,本质上使这个值具有引用语义,但他们没有这样做。我认为这样做的原因是没有特殊的语法来处理这个类型的值,所以It“没有理由让它们像Map、通道和切片一样工作。换句话说,吻。
推荐阅读:

xwbd5t1u

xwbd5t1u2#

The Go Programming Language Specification
电话
在函数调用中,函数值和参数按通常的顺序求值。求值后,调用的参数按值传递给函数,被调用函数开始执行。函数返回时,函数的返回参数按值传递回调用函数。
在Go语言中,一切都是按值传递的。
Rob Pike
在Go语言中,所有的东西都是按值传递的。
有一些类型(指针、通道、Map、切片)具有类似引用的属性,但是在这些情况下,相关的数据结构(指针、通道指针、Map头、切片头)持有指向底层共享对象(指向的东西、通道描述符、哈希表、数组)的指针;数据结构本身是通过值传递的。2总是这样。
一直都是

  • 抢劫
w8ntj3qf

w8ntj3qf3#

据我所知,Go语言,以及Java和C#从来没有C那样的过多的复制开销,但是没有解决所有权转移到容器的问题。因此,仍然涉及到复制。随着C变得更像是一种值语义语言,引用/指针被降级为i)类内部的智能指针托管对象和ii)依赖引用。move语义解决了过度复制的问题,注意这和“传值”无关,现在在C++中每个人都是通过引用(&)或者常量引用(const &)来传递对象的,让我们看一下这个(1):

BigObject BO(big,stuff,inside);
vector<BigObject> vo;
vo.reserve(1000000);
vo.push_back(BO);

或(2)

vector<BigObject> vo;
vo.reserve(1000000);
vo.push_back(BigObject(big,stuff,inside));

虽然你是通过引用传递向量vo,但在C03中向量代码中有一个拷贝。在第二种情况下,有一个临时对象必须被构造,然后被拷贝到向量中。因为它只能被向量访问,这是一个浪费的拷贝。
然而,在第一种情况下,我们的意图可能只是将BO的控制权交给向量本身,C
17允许这样做:
(1个,C++17)

vector<BigObject> vo;
vo.reserve(1000000);
vo.emplace_back(big,stuff,inside);

或(2,C++17)

vector<BigObject> vo;
vo.reserve(1000000);
vo.push_back(BigObject(big,stuff,inside));

从我所读到的内容来看,Java、C#或Go是否可以免除C++03在容器中所遭受的复制复制,这一点还不清楚。
老式的COW(写入时复制)技术也有同样的问题,因为一旦复制了向量中的对象,资源就会被复制。

v2g6jxz6

v2g6jxz64#

Stroustrup谈论的是C++,它允许您按值传递容器等-因此过度复制成为一个问题。
在Go语言中(像 Delphi 、Java等),当你传递一个容器类型等时,它们总是引用,所以这不是问题。无论如何,你不必处理它,也不必担心GoLang --编译器只是做它需要做的事情,从我目前所看到的来看,它做得很好。
感谢@KerrekSB让我走上了正确的道路。

  • @KerrekSB -我希望这是正确的答案。如果是错误的,你不承担任何责任。:)*

相关问题