• Go


    基本介绍
        在某些情况下,我们需要声明(定义)方法,比如Person结构体,除了有一些字段外(年龄,姓名...), Person结构体还有一些行为比如:可以说话,跑步...通过学习,还可以做算术题,这时就要用方法才能完成;
        Golang中的方法是作用在指定的数据类型上的(即,和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅是struct;
        基本使用:
            type Person struct {    
                    Name string      
                    Age  int  
            } 
            func (p Person) eat() {     
                    fmt.Println(p.Name, "可以吃东西~~~") 
            } 
             func main() {     
                    // 结构体的方法     
                    var p Person      
                    p.Name = "mary"     
                    p.Age = 30  
                    // 调用方法   
                    p.eat()    // mary 可以吃东西~~~
            }
            上面的一个说明:
                1.eat方法和Person 类型绑定;
                2.eat方法只能通过Person类型的变量来调用,而不能直接调用,也不能使用其他类型变量来调用。
                3.func (p Person)eat() {} ---> p 表示哪个Person变量调用,这个p就是他的副本,这点和函数传参非常相似。
                4. p这个名字,由程序员指定,不是固定;
     
    方法的调用和传参机制
            1.在通过一个变量去调用方法时,其调用机制和函数一样
            2.不一样的地方是,变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,则进行值拷贝,如果变量是引用类型,则进行地址拷贝)
     
    方法的声明(定义)
        func (recevier  type)  methodName (参数列表) (返回值列表){
                方法体
                return  返回值
        }
        参数列表,表示方法输入;
        receiver  type : 表示这个方法和type这个类型进行绑定,或者说该方法作用于type类型;
        receiver  type:  type 表示可以是结构体,也可以是其他的自定义类型;
        receiver: 就是type类型的一个变量(实例),比如:Person结构体的一个变量(实例)
        返回值列表,表示返回的值,可以多个
        方法主体,表示为了实现某一功能代码块
        return 语句不是必须的(有返回值列表就必须有return);
     
    方法注意事项和细节
        1.结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式;
        2.如果程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理
        3.Golang中的方法作用在指定的数据类型上的(即:和指定的数据类型绑定)因此自定义类型,都可以有方法,而不仅仅是struct, 比如int,float32等都可以有方法;
        4.方法的访问范围控制的规则,和函数一样,方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其他包访问;
        5.如果一个类型实现了String() 这个方法,那么fmt.Println 默认会调用这个变量的String() 进行输出;
        
        示例:
            type Circle struct {    
                    Radius float64 
            }
            // 为了提高效率,通常我们方法和结构体的指针类型绑定
            func (c *Circle) area() float64 {     
            // 因为 c 是指针,因此我们标准的访问其字段方式 是 (*c).Radius     
            // return 3.14 * (*c).Radius * (*c).Radius     
            // (*c).radius 等价  c.radius     
            c.Radius = 10     
            return 3.14 * c.Radius * c.Radius 
            }
     
            // 创建一个Circle 变量    
            var c Circle      
            // res := (&c).area()     
            // 编译器底层做了优化 (&c).area() 等价 c.area()     
            // 因为编译器会自动的给加上  &c     
            c.Radius = 7.0     
            res := c.area()     
            fmt.Println("面积是:", res)  // 面积是: 314     
            fmt.Println("c.Radius==", c.Radius)  // c.Radius== 10
     
        实现String方法:
            // 让student 结构体实现 String方法
            type Student struct {     
                    Name string      
                    Age  int  
            } 
            func (s *Student) String() string {     
                    str := fmt.Sprintf("Name=[%v] Age=[%v]", s.Name, s.Age)     
                    return str 
            }
            
            var s Student    
            s.Name = "smith"     
            s.Age = 30     
            // 没实现 String方法时,会打印出 &{smith 30}, 但是实现以后 Name=[smith] Age=[30]             
            fmt.Println(&s)
    练习:
        结构体实现乘法表打印与二维数组简单转置方法
        // 结构体 MethodUtils 
        type MethodUtils struct {     
                // 结构体也可以没有字段 
        }
        // 打印乘法表
        func (mu MethodUtils) cfb(n int) {     
                for i := 1; i <= n; i ++ {         
                        for j := 1; j <= i; j ++ {             
                                fmt.Printf("%v * %v = %v ", j, i, i * j)         
                        }         
                        fmt.Println()     
                } 
        } 
         // 转置 
        func (mu MethodUtils) zz(ewz [3][3]int) {     
            // 原始打印     
            for _, v := range ewz {         
                    for _, v2 := range v {             
                            fmt.Printf("%v ", v2)         
                    }         
                    fmt.Println()     
            }     
            // 转置打印     
            for i := 0 ; i < len(ewz); i++ {         
                    for j := 0; j < len(ewz[i]); j++ {             
                            fmt.Printf("%v ", ewz[j][i])         
                    }         
                    fmt.Println()     
            } 
        }
     
        // 实例化    
        var mu MethodUtils     
        // 方法调用     
        mu.cfb(9)     
        var ewz = [3][3]int {{1,2,3},{4,5,6},{7,8,9}}     
        mu.zz(ewz)
            
    方法与函数的区别
            1.调用方式不一样;
                函数的调用方式:  函数名(实参列表)
                方法调用方式:     变量.方法名(实参列表)
            2.对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然;
            3.对于方法(如struct的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以;
            不管调用形式如何,真正决定是值拷贝还是地址拷贝,看这个方法或函数定义的时候和哪个类型绑定的:
            如果和值类型,比如(p  Person) 则是值拷贝;如果是和指针类型,比如是(p *Person) 则是地址拷贝
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
            
  • 相关阅读:
    数据--第53课
    数据--第52课
    数据--第51课
    标准模板库中的优先队列(priority_queue)
    进程的定义
    进程的基本概念
    循环不变式
    插入排序
    模板声明
    标准模板库中的队列(queue)
  • 原文地址:https://www.cnblogs.com/guo-s/p/14032011.html
Copyright © 2020-2023  润新知