diff --git a/eBook/10.7.md b/eBook/10.7.md new file mode 100644 index 0000000..7361953 --- /dev/null +++ b/eBook/10.7.md @@ -0,0 +1,110 @@ +# 10.7 类型的String()方法和格式化描述符 + +当定义一个了有很多方法的类型时,十之八九你会使用String()方法来定制类型的字符串形式的输出,换句话说:一种可阅读性和打印性的输出。如果类型定义了String()方法,它会被用在fmt.Printf()中生成默认的输出:等同于使用格式化描述符%v产生的输出。还有fmt.Print()和fmt.Println()也会自动使用String()方法。 + +我们使用10.4中程序的类型来进行测试: + +Listing 10.22—method_string.go: +```go +package main + +import ( + "fmt" + "strconv" +) + +type TwoInts struct { + a int + b int +} + +func main() { + two1 := new(TwoInts) + two1.a = 12 + two1.b = 10 + fmt.Printf("two1 is: %v\n", two1) + fmt.Println("two1 is:", two1) + fmt.Printf("two1 is: %T\n", two1) + fmt.Printf("two1 is: %#v\n", two1) +} + +func (tn *TwoInts) String() string { + return "(" + strconv.Itoa(tn.a) + "/" + strconv.Itoa(tn.b) + ")" +} +``` + +输出: + + two1 is: (12/10) + two1 is: (12/10) + two1 is: *main.TwoInts + two1 is: &main.TwoInts{a:12, b:10} + +当你广泛使用一个自定义类型时,最好为它定义String()方法。从上面的例子也可以看到,格式化描述符%T会给出类型的完全规格,%#v会给出实例的完整输出,包括它的字段(在程序自动生成Go代码时也很有用)。 + +**备注:** + +不要在String()方法里面调用涉及String()方法的方法,它会导致意料之外的错误,比如下面的例子,它导致了一个无限迭代调用(TT.String()调用fmt.Sprintf,而fmt.Sprintf又会反过来调用TT.String()...),很快就会导致内存溢出: + +```go +type TT float64 + +func (t TT) String() string { + return fmt.Sprintf("%v", s) +} +t. String() +``` + +**练习**: + +练习 10.12:type_string.go + +给定结构体类型T: + +```go +type T struct { + a int + b float32 + c string +} +``` + +值t: `t := &{7, -2.35, "abc\tdef"}`。给T定义String(),使得`fmt.Printf("%v\n", t)`输出:`7 / -2.350000 / "abc\tdef"` + +练习 10.13:celsius.go + +为float64定义一个别名类型Celsius,并给它定义String(),它输出一个十进制数和°C表示的温度值。 + + +练习 10.14:days.go + +为int定义一个别名类型Day,定义一个字符串数组它包含一周七天的名字,为类型Day定义String()方法,它输出星期几的名字。使用iota定义一个枚举常量用于表示一周的中每天(MO,TU,...) + +练习 10.15:timezones.go + +为int定义别名类型TZ,定义一些常量表示时区,比如UTC,定义一个map,它将时区的缩写映射为它的全称,比如:`UTC -> "Universal Greenwich time"`。为类型TZ定义String()方法,它输出时区的全称。 + +练习 10.16:stack_arr.go / stack_struct.go + +实现栈(stack)数据结构: + +![](images/10.7_fig.jpg?raw=true) + +它的格子包含数据,比如整数i,j,k,l等等,格子从底部(索引0)之顶部(索引n)来索引。这个例子中假定n=3,那么一共有4个格子。 + +一个新栈中所有格子的值都是0. + +push将一个新值放到栈的最顶部一个非空(非零)的格子中。 + +pop获取栈的最顶部一个非空(非零)的格子的值。现在可以理解为什么栈是一个后进先出(LIFO)的结构了吧。 + +为栈定义一Stack类型,并为它定义一个Push和Pop方法,再为它定义String()方法(用于调试)它输出栈的内容,比如:`[0:i] [1:j] [2:k] [3:l]` + +(1). stack_arr.go:使用长度为4的int数据作为底层数据结构 +(2). stack_struct.go:使用包含一个索引和一个int数组的结构体作为底层数据结构,所以表示第一个空闲的位置。 +(3). 使用常量LIMIT代替上面表示元素个数的4重新实现上面的(1)和(2),是它们更具有一般性。 + +## 链接 +- [目录](directory.md) +- 上一节:[10.6 方法](10.6.md) +- 下一节:[10.8 垃圾回收和SetFinalizer](10.8.md) diff --git a/eBook/10.8.md b/eBook/10.8.md new file mode 100644 index 0000000..21a8df0 --- /dev/null +++ b/eBook/10.8.md @@ -0,0 +1,29 @@ +# 10.8 垃圾回收和SetFinalizer + +Go开发者不需要写代码来释放程序中不再使用的变量和结构占用的内存,在Go运行时中有一个独立的进程,即垃圾收集器(GC),会处理这些事情,它搜索不再使用的变量然后释放它们的内存。可以通过runtime包访问GC进程。 + +通过调用runtime.GC()函数可以显式的触发GC,但这只在某些罕见的场景下才有用,比如当内存资源不足时调用runtime.GC(),它会此函数执行的点上立即释放一大片内存,此时程序可能会有短时的性能下降(因为GC进程在执行)。 + +如果想知道当前的内存状态,可以使用: + +```go +fmt.Printf(“%d\n”, runtime.MemStats.Alloc/1024) +``` +上面的程序会给出已分配内存的总量,单位是Kb。进一步的测量参考:http://golang.org/pkg/runtime/#MemStatsType。 + +如果需要在一个对象obj被从内存移除前执行一些特殊操作,比如写到日志文件中,可以通过如下方式调用函数来实现: + +```go +runtime.SetFinalizer(obj, func(obj *typeObj)) +``` + +func(obj *typeObj)需要一个typeObj类型的指针参数obj,特殊操作会在它上面执行。func也可以是一个匿名函数。 + +在对象被GC进程选中并从内存中移除以前,SetFinalizer都不会执行,即使程序正常结束或者发生错误。 + +练习 10.17:从练习10.16开始(它基于结构体实现了一个栈结构),为栈的实现(stack_struct.go)创建一个单独的包stack,并从main包main.stack.go中调用它。 + +## 链接 +- [目录](directory.md) +- 上一节:[10.7 类型的String()方法和格式化描述符](10.7.md) +- 下一节:[11.1 什么是接口](11.1.md) \ No newline at end of file diff --git a/eBook/directory.md b/eBook/directory.md index 80cacac..ef2eb21 100644 --- a/eBook/directory.md +++ b/eBook/directory.md @@ -91,6 +91,8 @@ - 10.4 [带标签的结构体](10.4.md) - 10.5 [匿名字段和内嵌结构体](10.5.md) - 10.6 [方法](10.6.md) + - 10.7 [类型的String()方法和格式化描述符](10.7.md) + - 10.8 [垃圾回收和SetFinalizer](10.8.md) - 第11章:接口(interface)与反射(reflection) ## 第三部分:Go 高级编程 diff --git a/eBook/images/10.7_fig.jpg b/eBook/images/10.7_fig.jpg new file mode 100644 index 0000000..2baa9ec Binary files /dev/null and b/eBook/images/10.7_fig.jpg differ