• ChaoCai2010 置顶
    2019-03-13
    @ be humble
    我是作者,刚才给你的回复有些错字。

    你把String方法定义在结构上就可以了。

    这样结构变量和指向结构的指针变量都可以用。如果你定义在指针上则只有输出指向结构的变量时起作用。
    展开
    
     2
  • 面朝大海春暖花开
    2019-03-13
    每天最期待的一件事就是:老师视频有没有更新!言简意赅,重点突出,对想学go 的java程序员而言简直是是量身定制的,太喜欢了。

    作者回复: 谢谢你的鼓励!视频会一直持续更新的。

    
     4
  • 田佳伟
    2019-06-14
    老师,如果使用引用的方式调用struct,假如修改了结构体里边的元素值,会污染到其他线程或者协程吗?是线程安全的吗?

    作者回复: 看你传递的是不是指针,是指针就会。

    
     1
  • Dream.
    2019-03-23
    幸苦老师帮忙解答一下。

    为什么我定义一个链表temp:=new(ListNode),再定义了一个current:=temp

    current:=temp这样定义current的是值传递,current与temp本身的地址不是同一个地址,为什么他们的Next却是同一个地址呢?

    ============代码分割线=================
    package main

    import "fmt"

    //ListNode 定义一个链表结构
    type ListNode struct {
        Val int
        Next *ListNode
    }

    func main() {

        Linked()

    }

    //Linked 链表节点添加
    func Linked() *ListNode {

        temp := new(ListNode)
        fmt.Printf("temp address is %x\n", &temp)

        //为什么是值传递,Next的地址却是同一个地址?
        current := temp
        fmt.Printf("current address is %x\n", &current)

        current.Next = &ListNode{Val: 10, Next: nil}

        fmt.Printf("current.Next address is %x and Val is %d\n", &current.Next, current.Next.Val)

        fmt.Printf("temp.Next address is %x and Val is %d\n", &temp.Next, temp.Next.Val)

        temp.Next = new(ListNode)

        fmt.Printf("current.Next address is %x and Val is %d\n", &current.Next, current.Next.Val)

        fmt.Printf("temp.Next address is %x and Val is %d\n", &temp.Next, temp.Next.Val)

        return temp.Next
    }
    展开

    作者回复: 原因是因为你是通过new创建的temp,所以temp其实是一个指针值,current复制了temp的指针(只是用了一个新的地址来保存这个指针值)所以他们同时指向的同一个ListNode 的实例。你改成 temp=ListNode{}试试

    
     1
  • Flygar
    2019-03-14
    new返回指针(所以对于值类型: 数组,结构体;可以用new(T)来分配类型 T 的零值并返回其地址,也就是指向类型 T 的指针);仅分配空间;

    make(T) 返回类型 T (传入的T如果是引用类型,那么返回的也是引用类型) (所以对于引用类型(指针): Slice,Map,Channel;可以用make(T)来返回类型 T 并初始化);在堆上分配内存空间并初始化;

    &struct1{a, b, c} 是一种简写,底层仍然会调用 new () 表达式 new(Type) 和 &Type{} 是等价的。&Type底层仍会调用new()

    带指针(或值)参数的函数必须接受一个指针(或值);
    而以指针(或值)为接收者的方法被调用时,接收者既能为值又能为指针;
    任何类型都可以有方法;
    展开
    
     1
  • TERRY.ROD
    2019-12-10
    这里有个问题咨询下老师,为什么下面代码的C结构体类型会有两处注释地方会有panic,而B结构体不会,这块嵌套有点混乱,希望老师指导下

    // A
    type A struct{}
    func (a A) show() {
        fmt.Println("A call by show()")
    }
    func (a *A) ptShow() {
        fmt.Println("*A call by ptShow()")
    }
    // B
    type B struct {
        A
    }
    // C
    type C struct {
        *A
    }


    func main() {
        fmt.Println("test a, pa show:")
        a, pa := A{}, new(A)
        a.show()
        a.ptShow()
        pa.show()
        pa.ptShow()
        fmt.Printf("type of a(%T), pa(%T)\n", a, pa)

        //type B struct {
        //    A
        //}
        fmt.Println("\ntest b, pb show:")
        b, pb := B{}, new(B)
        b.show()
        b.ptShow()
        pb.show()
        pb.ptShow()

        //type C struct {
        //    *A
        //}
        fmt.Println("\ntest c, pc show:")
        c, pc := C{}, new(C)
        //c.show()    //panic c为C类型
        c.ptShow()
        //pc.show()    //panic pc为*C类型,
        pc.ptShow()
        fmt.Printf("type of c.A(%T), pc.A(%T)\n", c.A, pc.A)
    }
    展开

    作者回复: 这个是Go语言中比较令人困惑的部分,
    1. 如果嵌入类型是结构,则从外部类型可以访问定义在结构及结构引用上的方法
    2. 如果嵌入类型是结构的引用,则从外部类型仅可以访问定义在结构引用上的方法

    
    
  • york
    2019-11-29
    package main
    import "fmt"
    type bean struct {
        id string
    }
    func main() {
        map1 := map[string]string{"key":"value"}
        bean1 := bean{"hello world"}
        test1(map1, bean1 )
        fmt.Println(map1["key"])
        fmt.Println(bean1.id)
    }
    func test1(map1 map[string]string,bean1 bean) {
        map1["key"] = "newValue"
        bean1.id = "hello id"
    }
    输出结果:
    newValue
    hello world
    请问老师:为什么结构体bean进行了对象复制,而test1函数却仍然操作的是main函数中map对象。
    展开

    作者回复: 这是因为map是个结构,其中包含了指向数据的指针,当复制之后旧map中的指针值也被复制了,新map中的指针指向了同一个数据空间

     1
    
  • 木头发芽
    2019-11-21
    @说不要用关键字做函数名的同学,你是否是指函数名String(),这个其实是实现接口,duck type。类似于time.Time里的实现的String()打印时间
    
    
  • 疯琴
    2019-11-12
    老师,如果我定义String方法,会打印两个Inner Address和一个Outer Address,其中第一个Inner Address和其他两个Address不同。如果改成String1方法,就和您的演示一样了。此外如果定义String方法的时候传值,会打印4个不同的Address。请问这是为什么?跟Duck Type有关么?
    // 封装行为
    func (e *Employee) String() string {
        fmt.Printf("Inner Address is %x\n", unsafe.Pointer(&e.Name))
        return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
    }
    //调用测试
    fmt.Printf("Outer Address is %x\n", unsafe.Pointer(&e1.Name))
    t.Log(e1.String())
    展开

    作者回复: 不好意思,没有特别看懂你的问题。能否粘贴你的完整代码。

    
    
  • 虢國技醬
    2019-09-22
    通过这节课纠正了自己对值接受者和指针接受者一个误区:

    以前:
    两者区别:1.值接受者声明的方法,调用时使用这个值的副本,指针接受者声明的方法,调用时共享这个值;2. 指针值可以调用值接受者声明方法,值不能调用指针接受者声明方法
    现在:
    两者区别:值接受者声明的方法,调用时使用这个值的副本,指针接受者声明的方法,调用时共享这个值

    仔细验证发现不知道自己以前是在那里学到的第二点错误的知识,哎
    展开

    作者回复: 👍

    
    
  • 牛海朋
    2019-08-19
    既然指针方式没有内存复制,为啥还保留实例方式呢,有哪些场景需要用实例的方式
    
    
  • Geek_338030
    2019-08-12
    方法与函数有什么区别?
     1
    
  • X中倪
    2019-07-31
    老师能够通俗易懂的 讲解一下 Golang 中的行为吗 ?有点不懂,
    拿老师的案例来说,
    定义行为有两种方式:一种传递的是值 ,涉及到 内存的复制;一种是指针 不涉及到内存的复制。
    我自己在测试的时候 把函数和行为这两者搞混淆了 。多多指教
    
    
  • 杨宇铎(duo)
    2019-06-07
    老师,建议你不要使用关键词做函数名
    
    
  • 梧桐
    2019-06-05
    初学语言,使用中文解释了一遍,有助于理解
    ```

    type Employee struct {
        Id string
        Name string
        Age int
    }

    func TestCreateEmployeeObj(t *testing.T) {
        e := Employee{"001", "Tom", 17}
        e1 := Employee{Name: "Tom", Id: "001", Age: 17}
        e2 := new(Employee) //这里是实例化一个 Employee 结构,但是返回的是 引用|指针 => e:= &Employee{}
        e2.Age = 17
        e2.Id = "007"
        e2.Name = "Tom"
        t.Log(e, e1, e2)
    }

    // 1.这个方法调用实例时,这个实例会复制一份出来用于调用,就浪费内存了
    func (e Employee) SomrFunc1() string {
        fmt.Printf("⚠️:SomrFunc1注意,使用非指针方式调用实例时,指针位置是不一样的, 说明数据被拷贝了一份")
        fmt.Printf("\n非指针方式--- SomrFunc1 获取e.Name的地址 %x\n", unsafe.Pointer(&e.Name)) // 获取
        return fmt.Sprintf("\n ID:%s\n Name:%s\n Age:%d", e.Id, e.Name, e.Age)
    }

    // 2.这个方法用实例时,我们直接使用指针来读取数据,就避免来内存浪费
    func (e *Employee) SomrFunc2() string {
        fmt.Printf("⚠️:SomrFunc2注意,使用非指针方式调用实例时,指针位置是一样的, 使用指针访问的话,可以避免拷贝带来的内存开销")
        fmt.Printf("\n指针方式--- SomrFunc2 获取e.Name的地址 %x\n", unsafe.Pointer(&e.Name)) // 获取
        return fmt.Sprintf("\n ID:%s\n Name:%s\n Age:%d", e.Id, e.Name, e.Age)
    }

    func TestStructOperations(t *testing.T) {
        e := Employee{"0", "Tom", 17}
        fmt.Printf("\n非指针方式--- SomrFunc1 获取e.Name的地址 %x\n", unsafe.Pointer(&e.Name)) // 获取
        t.Log("\nSomrFunc1 Employee调用", e.SomrFunc1())

        fmt.Printf("\n指针方式--- SomrFunc2 获取e.Name的地址 %x\n", unsafe.Pointer(&e.Name)) // 获取
        t.Log("\nSomrFunc2 *Employee调用", e.SomrFunc2())

    }
    === RUN TestStructOperations

    非指针方式--- SomrFunc1 获取e.Name的地址 c000076370
    ⚠️:SomrFunc1注意,使用非指针方式调用实例时,指针位置是不一样的, 说明数据被拷贝了一份
    非指针方式--- SomrFunc1 获取e.Name的地址 c0000c4010

    指针方式--- SomrFunc2 获取e.Name的地址 c000076370
    ⚠️:SomrFunc2注意,使用非指针方式调用实例时,指针位置是一样的, 使用指针访问的话,可以避免拷贝带来的内存开销
    指针方式--- SomrFunc2 获取e.Name的地址 c000076370

    PASS
    ```
    展开

    作者回复: 赞

     1
    
  • 我爱北京天安门
    2019-04-09
    课程买下好久了 终于有空来学习了 刚开始有点不适应,现在觉得特别好,言简意赅突出重点。适合编程基础的人。。。
    
    
  • 芝士老爹
    2019-04-03
    用不用指针这2种方式,还发现一个不同点:
    不用指针的时候,t.Log(e)的输出也会改变,变成String()里面定义的格式,而用指针的时候就不会这样。
    测试函数代码:
    func TestStructOpration(t *testing.T) {
        e := Employee{"1", "kaozhengli", 32}
        t.Log(e)
        fmt.Printf("Address is %x\n", unsafe.Pointer(&e.Name))
        t.Log(e.String())
    }
    ## 需要注意一下地址输出的次数和t.Log输出的格式区别。
    方法一输出:
    === RUN TestStructOpration
    Address is c00007c100
    Address is c00007c0a0
    Address is c00009c2e0
    --- PASS: TestStructOpration (0.00s)
        /src/ch11/encapsulation/encap_test.go:48: ID:1-Name:kaozhengli-Age:32
        /src/ch11/encapsulation/encap_test.go:50: ID:1-Name:kaozhengli-Age:32
    方法二输出:
    === RUN TestStructOpration
    Address is c0000a02e0
    Address is c0000a02e0
    --- PASS: TestStructOpration (0.00s)
        /src/ch11/encapsulation/encap_test.go:48: {1 kaozhengli 32}
        /src/ch11/encapsulation/encap_test.go:50: ID:1=Name:kaozhengli=Age:32
    展开
    
    
  • 谁都别拦着我
    2019-04-01
    说传值传引用这个问题,其实go就没有引用一说,var a A是一个类型为A的值,&a所得的指针是一个类型为A*的值,传递的时候复制的内容是什么取决于参数的参数类型(当然也是调用函数时传入的参数类型),参数类型为A则复制A类型的内容,参数类型为A*则复制的是A*类型的内容(即一个内存地址)
    
    
  • manatee
    2019-03-26
    老师讲的浅显易懂,就连我这个门外汉也能听清楚。非常感谢。不知道老师是否有微信读者群可以让我加一下一起讨论呢?
    
    
  • 游子
    2019-03-14
    type Student struct {
        name string
        id int
        num string
    }

    func (stu Student) String() string {
        return fmt.Sprintf("String function called %s", stu.name)
    }

    func (stu *Student) String1() string {
        return fmt.Sprintf("String1 function called %s", stu.name)
    }

    func TestFunc(t *testing.T) {
        stu := Student{name: "zhang", id: 11}
        stu1 := Student{name: "John", id: 14}

        t.Log(stu.String1())
        t.Log(stu1)
    }

    结果输出:
    String1 function called zhang
    String function called John

    试了一下,函数名为String的时候,t.Log(stu1)会调用String
    展开
    
    
我们在线,来聊聊吧