3. Struct 与 面向对象 |《 刻意学习 Golang 》
go 的变量是 name type
, 老是写成 c 的 type name
go 的 type name struct
c 的typedef struct name
struct
对比 PHP:
PHP 中没有 struct
类型,这里对比 C 的 struct
- 支持匿名字段「嵌入字段」
- type = c 的 typedef
- 初始化有所区别
- 声明
- 初始化
- 访问
先看一下 C 中的 struct
// 结构体声明常用的两种
// 通过 typedef
typedef struct Human {
char* name;
int age;
} *pHuman;
// 直接声明
struct Human {
char* name;
int age;
};
struct Staff {
Human* human; // 指向结构体的指针
Human human; // 包含结构体
pHuman human; // 包含指向结构体的指针
int wage;
}
再看 Go
type Human struct {
name string
age int
}
var tom Human
// 通过赋值初始化
tom.name, tom.age = "Tom", 18
// 详细初始化
jerry := Human{age:25, name:"Jerry"}
// 按照结构体声明顺序
peter := Human{"Peter", 34}
// 通过 . 访问
fmt.Printf("%s is %d old\n", tom.name, tom.age)
匿名字段
匿名字段也叫「嵌入字段」顾名思义,嵌入到一个结构体的字段
只提供类型,而不写字段名。『通过这个来定义』
- 支持:类型、自定类型、结构体
- 如果是结构体:隐式的引入这个结构体的字段到新结构体
- 重复字段最外层优先原则,里层通过 隐式类型访问
type Staff struct {
Human // 隐式的引入 Human 的字段
wage int
age float32 // 覆盖 Human 的 age
}
tom := Staff{Human{"Tom", 18}, 1000, 18.5}
jerr := Staff{Human:Human{age:32, name:"Jerr"}, wage: 2000, age: 32.5}
fmt.Printf("%s is %f old,%d years, wage is %d", tom.name, tom.Human.age, tom.age, tom.wage)
fmt.Printf("%s is %f old,%d years, wage is %d", jerr.name, jerr.Human.age, jerr.age, jerr.wage)
面向对象
看完了 go 的面相对象后,emmmm... 感觉有点颠覆。首先是告别 class
。 然后一系列的 static public protected private
都没有了,甚至是 extends parent
都省了。我现在还很不习惯... 魔术方法?构造函数?...且边学边看
methd
"A method is a function with an implicit first argument, called a receiver."
这么理解呢,这么说
一个 method
就是 function 的第一个『参数』是一个 receiver
「接收者,接收这个函数的是谁即是方法的所有者」\
如果还没明白的话就再通俗一点
receiver = class
method = 成员函数
// 下面这个 function 是 ReceiverType (class) 的一个 method,
// funcName 首字母大写是公有、小写是私有
// ReceiverType 可以是结构体、自定义类型、内置类型「PS这个是真的骚」
func (r ReceiverType) funcName(parameters) {}
指针 作为 receiver
go 知道我们需要调用或传递的是指针,你写值他会自动帮你去用指针。理解这个先来理解下面 c 代码
void setZore(int* a) {
*a = 0;
// 在 go 中可以理解为
a = 0; // go 自动给你变成 *a = 0
}
int x = 10;
// 在 C 中必须传递地址 go 中可以是 setZore(x)
setZore(&x);
如果一个 method 的 receiver 是 *T, 你可以在一个 T 类型的实例变量 V 上面调用这个 method,而不需要 &V 去调用这个 method
如果一个 method 的 receiver 是 T,你可以在一个 T 类型的变量 P 上面调用这个 method,而不需要 P 去调用这个 method
继承跟重载
继承跟重载都跟匿名字段一样的
- 继承:如果匿名字段实现一个 method 包含这个匿名字段的结构体也能使用这个 method
- 重写:在包含者上面重新定义这个 method,重写匿名字段的方法
type Human struct {
name string
age int
}
type Employee struct {
Human
wage int
}
func (h *Human) Say() {
fmt.Printf("Hi, I am %s, My age is %d", h.name, h.age)
}
// Employee 重写 Say
func (e *Employee) Say() {
fmt.Printf("Hi, I am %s, %d old year, wage is %d per mothn", e.name, e.age, e.wage)
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
这继承好蛋疼的感觉
type Staff {
这里少了struct 吧@lovecn 『已纠正,感谢~』 我也是 觉得他这个很另类, 跟接触的面相对象都不一样。
@lovecn go 的面向对象 跟 接口 在设计上就有所区别,目前我还说不出这种设计跟传统我们了解的 之间有何优缺点。再完后看慢慢应该就能找到这么设计的精髓
老哥有
fmt.Printls()
这个函数吗?@Ali 是
fmt.Printf()
哈哈哈 笔误,谢谢~ 已纠正@jerrkill 另外还有我用你的代码执行,会报错 %s 的输出错误.,什么非法字符.
@Ali 能截图看下吗,还有具体代码,我在我电脑上是执行通过的
@jerrkill 可以我也是刚学,也可能是我 语法上有错误.希望指正. 感觉像是覆盖 Human 的 age 的错误.但我换了 int 也不行.
代码如下:
输出结果如下:
@Ali

如上图
@jerrkill 谢谢 ! 我懂了,主要还是对 %f %d %s 不了解啊. 我去查询下资料补补去.
那你的不用指定几位浮点吗?
@Ali
%.1f
保留一位小数%.2f
两位,就这样。如果没有设置保留小数位的话默认是 6 位。32 位浮点数跟 64 位浮点数都用
%f
默认是保留 6 位小数,这个应该在 fmt 包里面的Printf()
里面可以看源码。正准备接下来分析标准库跟包,并写在《刻意学习 golang》 里面来。欢迎关注哈~
@jerrkill 必须关注啊.我流程式的学习,看到你文章,就像是我自己的总结一样,甚至还有我没学到的 一些知识.
这样子的话,应该是因为电脑环境的原因.我的 MacOS .
@Ali
%f
是用来格式化float
的,而jerr.Human.age
是 int 类型,所以会报错@jerrkill 嗯嗯.我是把顺利搞混了. 哈哈.