golang(3)

前端之家收集整理的这篇文章主要介绍了golang(3)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

八,struct

struct的定义如下: type 结构体名 struct{},其中定义的变量不要var,但是仍然是倒序。
结构体变量的初始化:结构体名{}括号中的变量按照定义的顺序依次填写;如果不想写全,或者不想按顺序写,还可以写成json的格式。(这个为后续添加变量,而无需修改赋值提供了方便);
匿名变量的访问:在S1中有一个匿名变量S,对于S中的变量的访问可以直接写a.name如13行所示;当然也可以写成a.S.name(其变量明就是S); 如果S1中又定义了name,写全可以访问到S中的变量了;
由于*S和S中的变量的访问方法相同,所以不能同时存在一个S一个*S的匿名变量;

点击(此处)折叠或打开

  1. package main

  2. type S struct{
  3. ageint
  4. name string
  5. }
  6. type S1 struct{
  7. S
  8. }
  9. func main() {
  10. var a S1=S1{S{10,"tom"}}
  11. //var a S1=S1{Sname:"tom"}}
  12. println(a.name);
  13. }

函数&方法

函数的声明和C也是差不多的。就是变量的类型和返回值都是写在后面的。还有可以有多个返回值哦。
函数带上receiver就变成方法了,如第10行所示;

点击(此处)折叠或打开

  1. package main

  2. type iface interface {
  3. setName()
  4. }
  5. type S1 struct {
  6. name string
  7. }

  8. func (s *S1) setName() {
  9. s.name = "hello"
  10. }

  11. func main() {
  12. var a S1
  13. (*S1).setName(&a) //a.setName()
  14. println(a.name)
  15. }
16行有两种写法:通过汇编查看,可以发现这两者指示写法不同,实际是等价的。都是调用了callq 0x400c00 <main.(*S1).setName>方法;其中a.setName被编译器自动换成了(&a).setName;
这就a.setName的调用发发有点类似面向对象的写法了。如果将地10行的*去掉。则调用函数是会创建一个临时的S1对象。让后再对其赋值。等函数退出后,之际调用的便量并没有得到修改。那么这个函数还有什么作用呢? 为什么还要提供写法的?


当然由于slice的特殊性,当slice作为参数时,其内容被更改了,还是可以反应到实际数组中去的。如下面的代码,实际打印结果是10;因为此处传值指示传的slice本身,而其指向的数组还是同一个。要想让b指向另一个数组,就必须得用指针型了。
还有注意点,就是原始类型不能直接作为receiver;

点击(此处)折叠或打开

  1. package main
  2. type A []int
  3. func (s A) setValue() {
  4. s[0] = 10
  5. }
  6. /*func (s *A) setValue() {
  7. (*s)[0] = 10 //注意,此处必须带*号,编译器已经无法自动转换了。如上例的第11行;
  8. }*/
  9. func main() {
  10. var a = [...]int{1, 2, 3, 4, 5, 6, 7, 8}
  11. b := A(a[1:6])
  12. b.setValue()
  13. println(a[1])
  14. }

十,interface

上面的例子里面已经用到了interface;和java相同,interface是一个定义了一系列方法的集合。但是与java不同的是,此处的interface不需要明确标明implents,只要一个类型的被作为receiver实现了所有的interface的方法,编译器就能自动识别该类型实现了该interface;
如下面的代码:S1实现了myface;由代码的19,20行可以发现,无论是送入S1的指针,还是对象,都能实现对setName的调用,如果setName的receiver是*S;则20行会报错。这就应证了golang手册中的一句话,T的方法集包含receiver为T 和*T的所有方法,而*T的方法集只包含receiver为*T的方法;(更通俗的表达方法时,当参数(receiver是T)时,调用方法的对象既可以时T,也可以时*T; 当receiver为*T时,调用时的参数只能时*T; 所以当interface作为参数时,具体是传入T还是*T。需要看该对象的具体实现,如果该对象的receiver只有*T,则参数只能传入*T,否则可以任选T或者*T; 当然只有当receiver为*T,且参数传入*T时,才可能真正修改到T对象的成员变量;

点击(此处)折叠或打开

  1. package main

  2. type myface interface {
  3. setName()
  4. }
  5. type S1 struct {
  6. name string
  7. }

  8. func (s S1) setName() {
  9. s.name = "hello"
  10. }
  11. func test(a myface) {
  12. a.setName()
  13. }

  14. func main() {
  15. var a, b S1
  16. test(&a)
  17. test(b)
  18. println(a.name)
  19. println(b.name)
  20. }
下面关心的是,在test函数中a是个什么东西?在调用test(&a)时,14行的a为:{tab = 0xc200035000,data = 0xc20001b010};第一个参数不知道干吗的,但是data中的值是19行 info loacals 时,系统显示的&a的值;
当20行的掉用进入到14行时:a的内容为:{tab = 0xc200035030,data = 0xc20001b020},data是一个紧依赖于上面的一个空间。

由上面两个联合得知在19行运行info locals的输出
(gdb) i locals
b = {name = {str = 0x0,len = 0}}
&a = 0xc20001b010

以及:p &b.name
$11 = (struct string *) 0x7fffe7f73f58
结合上面的结果可以看出此处输出的&a不是a的地址,而是某个中间状态得东西,这个和test(b)进入test函数后的内容类似;
经过查看内存发现 当今如14行时,此处的a的大小是固定的只包含两个指针的变量,所以sizeof(a)=2*sizeof(void*);而*(a.data)是一个内容空间,而该空间实际是test,传值时的一个拷贝;


下面在看看*T的receiver的结果:

点击(此处)折叠或打开

  1. package main

  2. type myface interface {
  3. setName()
  4. }
  5. type S1 struct {
  6. name string
  7. age int
  8. }

  9. func (s* S1) setName() {
  10. s.age=11
  11. }
  12. func test(a myface) {
  13. a.setName()
  14. }

  15. func main() {
  16. var a, b S1 = S1{name:"tom"}, S1{name:"java"}
  17. test(&a)
  18. test(&b)
  19. println(a.name)
  20. println(b.name)
  21. }
此时15行a.data的值就是&a的地址;所以说interface 作为参数时,其有两个field,第二个field,data其中包含的是参数的地址,/* 不同的是 在*T的method中,data的值直接位传入参数的值,而T的method中是指向参数的一个拷贝*/,不同的是,当传入的参数时指针时,data直接指向该指针的位置(data的值就是参数的地址),当传入的参数是对象时,该对象会被复制一份,然后data指向该新复制的对象,即其值是新对象的地址;


点击(此处)折叠或打开

  1. package main

  2. type myface interface {//接口,只有一个方法
  3. setName()
  4. }
  5. type S1 struct { //结构体S1
  6. name string
  7. }
  8. type S2 struct { //结构体S2
  9. name string;
  10. }

  11. func (this S2)setName(){ //S2的receiver是T;
  12. this.name="s2";
  13. }

  14. func (s *S1) setName() { //S1的receiver是*T;
  15. s.name = "S1"
  16. }

  17. func test1(a myface){
  18. a.setName(); //调用接口的方法
  19. }

  20. func main(){
  21. a2:=S2{name: "aS2"}
  22. a1:=S1{name:"aS1"};
  23. //b1:=S1{name: "bS1"}
  24. b2:=S2{name:"bS2"}
  25. test1(&a1) //S1的receiver是*T,所以值接受*T参数;
  26. //test1(b1)
  27. test1(&a2) //S2的receiver是T,所以接受*T;
  28. test1(b2) //S2的receiver是T,所以也接受T;
  29. println("a1.name=",a1.name); //S1,这是因a1的内存地址最终能传到*S1.setName;
  30. println("a2.name=",a2.name); //aS2(没有变),a2的地址虽然能到达test1,但是S2.setName中又复制了一个变量;
  31. println("b2.name=",b2.name); //bS2(没有变),b2到达test1时已经复制了一份,但是到s2的setName又复制了,总计复制两遍。肯定达不到效果
  32. }
对于interface的结构,今天在官方文档中找到了说明: A variable of interface type stores a pair: the concrete value assigned to the variable,and that value's type descriptor. To be more precise,the value is the underlying concrete data item that implements the interface and the type describes the full type of that item
详细参见:http://golang.org/doc/articles/laws_of_reflection.html; 或者其中的blog:http://research.swtch.com/interfaces
原文链接:https://www.f2er.com/go/190861.html

猜你在找的Go相关文章