Golang复制包含指针

时间:2017-04-25 23:07:09

标签: pointers memory go ownership

TL; DR 由于赞成票,缺乏答案和评论,我认为需要一个博士;博士是需要的。

如何在golang中创建一个包含指针的结构,然后通过值安全地将其传递给其他函数? (安全地我的意思是,不必担心这些函数可以取消引用所述指针并更改其指向的变量)。

如果您要给出的答案是“复制功能”,那么如何删除原始复制构造函数/运算符?用我的自定义复制功能覆盖它?或者阻止人们使用它?

在golang中,我可以拥有一个包含指向动态分配变量的指针的结构。

我也可以将这些结构的实例传递给“复制”它们的函数。

但是,我无法覆盖或删除内置复制操作符。这意味着,理论上,我可以使用以下代码:

import (
        "fmt"
)

type A struct {
        a * int
}

func main() {
        var instance A
        value := 14
        instance.a = &value
        fmt.Println(*instance.a) // prints 14
        mutator(instance)
        fmt.Println(*instance.a) // prints 11 o.o
}

func mutator(instance A) {
        *instance.a = 11
        fmt.Println(*instance.a)
}

这种类型的代码在这里显然有点荒谬。但是,假设成员字段“a”是一个复杂的结构,它可能代表访问它的函数将尝试修改它。

一旦调用函数“mutator”,程序员可能希望继续使用他的A实例并且(假设他不一定编码结构或者知道它的内部结构)甚至可能假设他传递的是副本而不是指针,他的A实例将保持不变。

现在,有几种流行的语言允许程序员考虑分配和操作非golang的内存。我不知道Rust或C所以我将不会在C ++中解决这个问题:

a)假设我是A类的设计者,我可以构建一个拷贝构造函数,产生以下代码:

#include <iostream>

    class A {
    public:
            int * a;
            A(int value): a(new int{value}) {}
            A(const A & copyFrom): a(new int{*copyFrom.a}) {}
    };

    void mutator(A instance) {
            *instance.a = 11;
            std::cout << *instance.a << "\n";
    }


    int main() {
            A instance{14};
            std::cout << *(instance.a) << "\n";
            mutator(instance);
            std::cout << *instance.a << "\n";
    }

这允许复制我的类的实例,并添加指针也被重新分配的警告。

b)假设我是A类的设计者,并且不想构建复制构造函数(假设任何一个点可能非常大或者A经常在性能关键条件下用作只读对象)但是想要确保复制的任何赋值都不能修改a指向的值(但仍允许人们通过将其赋值给新值来修改它)我可以像这样编写我的类:

class A {
public:
        const int * a;
        A(int value): a(new const int{value}) {}
};

这会使以下代码无法编译:

void mutator(A instance) {
        *instance.a = 11;
        std::cout << *instance.a << "\n";
}


int main() {
        A instance{14};
        std::cout << *(instance.a) << "\n";
        mutator(instance);
        std::cout << *instance.a << "\n";
}

但是下面的代码编译得很好:

void mutator(A instance) {
        instance.a = new const int{11};
        std::cout << *instance.a << "\n";
}


int main() {
        A instance{14};
        std::cout << *(instance.a) << "\n";
        mutator(instance);
        std::cout << *instance.a << "\n";
}

现在,请注意,这是典型的C ++的“面向对象”(eegh)设计。如果我能在函数签名中使用某种规则来保证不会对传递给它的A实例进行修改,或者为了动态地声明A“const”和“guard”的实例,我会更喜欢分配字段(不仅是静态字段)以防止重新分配。

然而,虽然解决方案可能并不完美,但它是一种解决方案。它允许我清楚地了解我的A实例的“所有权”。

在golang中,似乎包含指针的实例的任何“副本”基本上都是免费的,即使结构的作者有这样的意图,它也无法安全地传递。

我能想到的一件事就是拥有一个“复制”方法,它返回一个全新的结构实例(类似于上面例子中的Copy Constructor)。但是如果没有删除复制构造函数/运算符的能力,就很难确保人们会使用和/或注意它。

老实说,golang似乎很奇怪golang甚至允许重写指针的内存地址而不使用“unsafe”包或类似的东西。

像许多其他人一样,简单地禁止这种类型的操作会不会更有意义吗?

考虑到“追加”的工作方式,似乎很明显,作者的意图是支持将新变量重新分配给指针,而不是改变之前指向的变量。然而,尽管使用内置结构(如切片或数组)很容易实现,但使用自定义结构(似乎没有,至少将所述结构包装在包中)似乎很难实现。

我是否忽略了在golang中复制构建(或禁止复制)的方法?当记忆和时间允许时,作者的初衷是鼓励重新分配而不是变异吗?如果是这样,为什么变异动态分配变量如此容易?有没有办法模仿结构或文件的私有/公共行为,而不是完整的包?有没有其他方法可以强制执行一些相似的所有权与结构,我会忽略指针?

1 个答案:

答案 0 :(得分:1)

  

如何在golang中创建一个包含指针的结构   安全地将它传递给其他函数? (安全地我的意思是没有   不得不担心这些函数可以取消引用指针和   改变它指向的变量。)

将导出的包类型与未导出的字段一起使用。例如,

src/ptrstruct/ptrstruct.go

package ptrstruct

type PtrStruct struct {
    pn *int
}

func New(n int) *PtrStruct {
    return &PtrStruct{pn: &n}
}

func (s *PtrStruct) N() int {
    return *s.pn
}

func (s *PtrStruct) SetN(n int) {
    *s.pn = n
}

func (s *PtrStruct) Clone() *PtrStruct {
    // make a deep clone
    t := &PtrStruct{pn: new(int)}
    *t.pn = *s.pn
    return t
}

src/ptrstruct.go

package main

import (
    "fmt"

    "ptrstruct"
)

func main() {
    ps := ptrstruct.New(42)
    fmt.Println(ps.N())
    pc := ps.Clone()
    fmt.Println(pc.N())
    pc.SetN(7)
    fmt.Println(pc.N())
    fmt.Println(ps.N())
}

输出:

src $ go run ptrstruct.go
42
42
7
42
src $ 
  

如果您要给出的答案是“复制功能”,那我该怎么办?   删除原始拷贝构造函数/运算符?跟我一起改写它   自定义复制功能?或者阻止人们使用它?

停止使用C ++编程;在Go中开始编程。根据设计,Go不是C ++。

“复制构造函数/运算符”和“用自定义函数覆盖它”是C ++概念。

参考文献:

The Go Programming Language Specification

Blocks

Declarations and scope

Exported identifiers