电子烟网站设计辽宁省品牌建设的建议

当前位置: 首页 > news >正文

电子烟网站设计,辽宁省品牌建设的建议,建网站服务,wordpress自定义字段怎么用面向对象——结构 Go 仅支持封装#xff0c;不支持继承和多态#xff1b;继承和多态要做的事情交给接口来完成#xff0c;即——面向接口编程。Go 只有 struct#xff0c;没有 class。 定义一个最简单的树节点#xff08;treeNode#xff09;结构#xff0c;方法如下不支持继承和多态继承和多态要做的事情交给接口来完成即——面向接口编程。Go 只有 struct没有 class。 定义一个最简单的树节点treeNode结构方法如下 package mainimport fmttype treeNode struct {value intleft, right *treeNode }func main() {var root treeNodefmt.Println(root) } 输出的结果是 {0 nil nil}如果想赋予 treeNode 初值则可以按照下述方式来对 treeNode 进行初始化 var root treeNoderoot treeNode{value: 3} root.left treeNode{} root.right treeNode{5, nil, nil} root.right.left new(treeNode)需要注意的是Go 的 struct 使用花括号进行值初始化而没有构造函数的概念。 注意最后一行的 new 方法它是 Go 的内置方法其行为非常类似于 C 的 new 关键字即新划分一个动态内存并返回类类型对应的指针。 还有一点要注意的是最后一行的root.right.left是指针在 C 中应该使用root.right - left来对指针所指对象的成员进行访问但是在 Golang 当中没有这么严格的要求。可以总结为无论是地址还是指针均使用 . 来访问成员。 可以将 struct 定义在 slice 当中方法也非常的简单声明一个对应类型的 slice 并使用花括号进行初始化即可 nodes : []treeNode{{value: 3},{},{6, nil, root}, } // 注意, 在花括号初始化当中可以省略 treeNode 关键字 fmt.Println(nodes)输出的结果为 [{3 nil nil} {0 nil nil} {6 nil 0xc0000080d8}] // 最后一个地址因操作设备而异结构有构造函数吗 Golang 的 struct 没有构造函数这一说法。 当需要构造函数时可以构建一个工厂函数来进行替代 func createNode(value int) *treeNode {// 使用工厂函数来构建结构对象, 返回一个结构对象的指针return treeNode{value: value} }需要注意的是从 C 的角度来看上述代码是非常典型的错误因为它试图返回局部变量的地址给外部。 但是在 Golang 当中上述语句是合法的这也是 Golang 和 C 较大的区别即 Golang 可以在局部返回变量的地址。 C 将变量的内存分配在栈上当新建动态内存时需要开发者自己进行资源的调度和销毁。而 Java 将变量的内存分配在堆上支持垃圾回收机制。而我们不需要关心 Golang 将内存分配在栈上还是堆上编译器会帮助我们管理。当局部变量的地址被返回时编译器将内存分配在堆上否则分配在栈上。 结构有成员函数吗 Golang 的结构同样没有成员函数但是 Golang 具有一种函数定义的语法糖通过下述方式对函数进行定义则结构对象可以直接以成员访问的形式对函数进行调用但本质上该函数不是结构的成员函数这类函数被称作结构的方法 func (node treeNode) print() {fmt.Println(node.value) }关键字 func 后面的圆括号当中的 (node treeNode) 被称作函数的接收者接收者有两种类型分别是值接收者和指针接收者。需要指针接收者的原因是Golang 的函数只有值传递因此如果希望在结构的方法中对结构的值进行修改或是在一个实现了较大型功能的函数调用上确保效率则需要使用指针接收者的形式来对结构的方法进行定义。 首先来看上述值接收者方法的调用 直接调用 root.print()即可完成 root 对应成员 value 的值的打印。 一个典型的指针接收者的例子如下在该例中我们试图修改 node 的 value func (node *treeNode) setValue(value int) {node.value value }之前我们已经提到无论结构对象此时是值还是指针访问其成员的方法都是使用 . 。 注意nil 指针也可以调用方法。在 Go 当中nil 是一个安全的可以调用结构的方法的指针。 但是在方法的具体实现上虽然 nil 可以安全地调用方法但是在方法中不能对 nil 指针指向对象的成员进行修改因此可以对 setValue 进行修改 func (node *treeNode) setValue(value int) {if node nil {fmt.Println(Setting value to nil node. Ignored)return}node.value value }实验如下 var pRoot *treeNode pRoot.setValue(200) pRoot root pRoot.setValue(300) pRoot.print()输出为 Setting value to nil node. Ignored 300由于 nil 指针仍然可以调用结构的方法因此可以方便地实现树的中序遍历 func (node *treeNode) traverse() {if node nil {return}node.left.traverse() // 不需要判断 left 是否为空指针, 即使是空指针也可以调用node.print()node.right.traverse() }值接收者是 Go 特有的值/指针接收者均可接受值/指针。 包和封装 在 Go 当中名字一般使用 CamelCase首字母大写代表 public首字母小写代表 private 此处的 public 和 private 是针对包package来说的。 每个目录只有一个包main 包 包含可执行入口为结构定义的方法必须放在同一个包内可以在包内的不同文件对结构的方法进行定义 现在我们在目录下新建一个名为 entry 的目录并将 node.go 当中的 main 函数迁移到 entry 目录下的 entry.go 当中。注意将 entry.go 的 package 改为 main。 然后修改 node.go 当中的结构成员和结构方法首字母为大写以代表它们是 public 的。 文件目录的组织方法如下 entry.go 当中的内容是 package mainimport learngo/treefunc main() {var root tree.Noderoot tree.Node{Value: 3}root.Left tree.Node{}root.Right tree.Node{5, nil, nil}root.Right.Left new(tree.Node)root.Left.Right tree.CreateNode(2)root.Right.Left.SetValue(4)root.Traverse() } node.go 当中的内容是 package treeimport fmttype Node struct {Value intLeft, Right *Node }func (node Node) Print() {fmt.Print(node.Value, ) }func (node *Node) SetValue(value int) {if node nil {fmt.Println(Setting Value to nil node. Ignored)return}node.Value value }func CreateNode(value int) *Node {// 使用工厂函数来构建结构对象, 返回一个结构对象的指针return Node{value, nil, nil} }func (node Node) Traverse() {if node nil {return}node.Left.Traverse()node.Print()node.Right.Traverse() } 需要注意的是将结构 TreeNode 修改为 Node因为结构的名字不宜与包名 tree 前缀相重复。虽然包名与结构名的前缀重复并不会报错但是这样会降低代码的可读性。 扩展已有的类型 我们已经知道为结构定义的方法必须放在同一个包内并且可以放在不同的文件当中。那么如果现在有一个已知类型它的作者不是我们但我们想对它进行拓展此时应该怎么做呢 由于 Go 语言没有继承想要扩充系统类型或已知的自定义类型的方法有三种分别是 定义别名使用组合使用内嵌Embedding 使用组合 假定我们现在希望对二叉树进行后序遍历但是 tree 当中仅实现了中序遍历一个可行的方法是将 Node 结构进行包装变为 myTreeNode / tree.Node 只实现了中序遍历, 现在我们希望实现后序遍历 */ type myTreeNode struct {node *tree.Node }之后我们对 myTreeNode 通过指针接收者的方式实现后序遍历 func (myNode *myTreeNode) postOrder() {if myNode.node nil || myNode.node nil { // 需要判断 myTreeNode 的成员是否为 nil 指针return}left : myTreeNode{myNode.node.Left} // 注意, 成员初始化应使用花括号而非圆括号left.postOrder()right : myTreeNode{myNode.node.Right}right.postOrder()myNode.node.Print() }在 main 函数中的调用 node : myTreeNode{root} node.postOrder() fmt.Println()定义别名 我们已经知道可以使用 slice 来实现 int 类型的队列。 我们可以使用关键字 type 定义 []int 的别名将其命名为 queue package queuetype Queue []int // 目前的 queue 是一个 int 的 slicefunc (q *Queue) Push(v int) {*q append(*q, v) // q 指向的 slice 被改变了 }func (q *Queue) Pop() int {head : (*q)[0]*q (*q)[1:]return head }func (q *Queue) IsEmpty() bool {return len(*q) 0 } 实验如下 package mainimport (fmtlearngo/queue )func main() {q : queue.Queue{1}q.Push(2)q.Push(3)fmt.Println(q.Pop())fmt.Println(q.Pop())fmt.Println(q.IsEmpty())fmt.Println(q.Pop())fmt.Println(q.IsEmpty()) } 使用内嵌Embedding 使用内嵌进行类型拓展的示例如下 /*tree.Node 只实现了中序遍历, 现在我们希望实现后序遍历使用内嵌的方式来实现已知类型的拓展 */ type myTreeNode struct {// 直接省略类型的变量名, 即可完成内嵌// 之前的定义是 node *tree.Node, 使用 node 作为结构的成员变量// 在使用内嵌进行拓展时, 不需要使用 node*tree.Node // Embedding// 语法糖, 节省代码量 }func (myNode *myTreeNode) postOrder() {if myNode.Node nil || myNode.Node nil {return}left : myTreeNode{myNode.Left} // 可以注意到, 使用内嵌之后, 可以直接访问 tree.Node 的成员变量以及方法left.postOrder()right : myTreeNode{myNode.Right}right.postOrder()myNode.Print() }内嵌看起来与 C 当中的继承非常的相似但是本质上内嵌和继承还是有去别的。需要重点强调的是Go 没有继承和多态只有封装。 假设现在我们要使用 myTreeNode 类型实现一个 Traverse在其它语言当中该行为会被视为重载在 Go 中Node 内嵌在了 myTreeNode 当中而在其它语言当中会被视为继承关系。 IDE 会提示我们“转到阴影方法” 假设现在的实现为 func (myNode *myTreeNode) Traverse() {fmt.Println(this method is shadowed) }则使用 myTreeNode 对 Traverse 进行调用的结果如下 this method is shadowed确保 myTreeNode 调用 Node 的 Traverse() 方法的做法是显式地调用root.Node.Traverse()即显式地对嵌入的方法进行调用。 继承的一个非常有用的性质是可以将一个基类指针与派生类对象进行关联但是在 Go 当中是不可行的 因此内嵌在本质上是一个语法糖内嵌的类型与结构类型没有关联。 Golang 通过接口来实现类似于继承的做法即——面向接口编程。