仓颉入门:struct结构类型

在C语言,C++中都有struct和class两个关键字,但是struct的使用在不同语言中的限制也有一定差异,这篇看一下struct在仓颉中的使用。

定义 struct 类型

struct 类型的定义以关键字 struct 开头,后跟 struct 的名字,接着是定义在一对花括号中的 struct 定义体。struct 定义体中可以定义一系列的成员变量、成员属性、静态初始化器、构造函数和成员函数。
class 与 struct 的主要区别在于:class 是引用类型,struct 是类型,它们在赋值或传参时行为是不同的;class 之间可以继承,但 struct 之间不能继承。

struct 成员变量

struct 成员变量分为实例成员变量和静态成员变量,实例成员变量只能通过 struct 实例访问,静态成员变量只能通过 struct 类型名访问。
实例成员变量定义时可以不设置初值,也可以设置初值。

struct Person{
    static let sex:Rune = '男'
    let name:String 
    let age:Int64 = 18
    public Person(name:String){
        this.name = name
    }
    public func show(){
        println('${this.name} ${sex} ${this.age} 岁')
    }
}
main() {
    let person = Person('张三')
    person.show()  //张三 男 18 岁
}

struct 静态初始化器

静态初始化器以关键字组合 static init 开头,后跟无参参数列表和函数体,且不能被访问修饰符修饰。函数体中必须完成对所有未初始化的静态成员变量的初始化,否则编译报错。
一个 struct 中最多允许定义一个静态初始化器

struct Person{
    static let sex:Rune = '男'
    static var height:Int64 
    static let weight:Int64
    static init(){
        height = 180
        weight = 65
    }
    let name:String 
    let age:Int64 = 18
    public Person(name:String){
        this.name = name
    }
    public func show(){
        println('${this.name} ${sex} ${this.age} 岁')
    }
}

struct 构造函数

普通构造函数

普通构造函数以关键字init 开头,后跟参数列表和函数体,函数体中必须完成对所有未初始化的实例成员变量的初始化
一个 struct 中可以定义多个普通构造函数,但它们必须构成重载

struct Rectangle {
    let width: Int64
    let height: String
    public init(){
        this.width=1
        this.height=''
    }
    public init(width: Int64) {
        this.width = width
        this.height = ''
    }
    public init(height: String) {
        this.width = 1
        this.height = height
    }
    public init(width: Int64, height: String) { 
        this.width = width
        this.height = height
    }
}

主构造函数

可以定义(最多)一个主构造函数。主构造函数的名字和 struct 类型名相同,它的参数列表中可以有两种形式的形参:普通形参和成员变量形参(需要在参数名前加上 let 或 var),成员变量形参同时扮演定义成员变量构造函数参数的功能。

struct Location{
    let tag:String
// 主构造参数的成员变量形参,定义了 lat 和 lon 2个成员变量,并且赋值
    public Location(tag:String,public var lat:Int64,private var lon:Int64){
        this.tag = tag
    }
    public func show(){
        println('location lat:${this.lat} lon:${this.lon}')
    }
}
main() {
    let location = Location('tag',110,119)
    println(location.lat)  //110
    println(location.tag)  //tag
    location.show()  //location lat:110 lon:119
}

struct 成员函数

实例成员函数

实例成员函数只能通过 struct 实例访问,在实例成员函数中可以访问静态成员变量以及静态成员函数。

静态成员函数

静态成员函数使用 static 修饰符修饰,只能通过 struct 类型名访问。静态成员函数中不能访问实例成员变量,也不能调用实例成员函数

struct Rectangle {
    let width: Int64 = 10
    let height: Int64 = 20
    public func area() {  //实例成员函数
        typeName() // 可以访问静态成员函数
        return this.width * this.height
    }
    public static func typeName(): String {
        // "${this.height}" //non-static variable 'this' cannot be referenced from a static context
        'Rectangle'
    }
}
main() {
    let rec = Rectangle()
    println(rec.area())  //通过 struct 实例访问
    println(Rectangle.typeName()) // 通过 struct 类型名访问
}

struct 成员的访问修饰符

struct的成员(包括成员变量、成员属性、构造函数、成员函数、操作符函数)用 4 种访问修饰符修饰:
private 表示在 struct 定义内可见。
internal表示仅当前包及子包内可见。
protected 表示当前模块可见。
public 表示模块内外均可见。

禁止递归 struct

//正确
struct R0{

}
struct R{
    let r:R0=R0()
}
// 错误
struct R1 { // Error, 'R1'递归地引用自身
    let other: R1
}
struct R2 { // Error, 'R2' 和 'R3' 是相互递归的
    let other: R3
}
struct R3 { // Error, 'R2' 和 'R3' 是相互递归的
    let other: R2
}

创建 struct 实例

在 struct 定义之外,通过 struct 类型名调用构造函数创建该类型实例,并可以通过实例访问满足可见性修饰符(如public )的实例成员变量和实例成员函数。
如果希望通过 struct 实例去修改成员变量的值,需要将 struct 类型的变量定义为可变变量,并且被修改的成员变量也必须是可变成员变量(使用 var 定义)。

struct Person{
    private static let sex:Rune = '男'
    static var height:Int64 
    static let weight:Int64
    static init(){
        height = 180
        weight = 65
    }
    var name:String 
    var age:Int64 = 18
    public Person(name:String){
        this.name = name
    }
    public func show(){
        println('${this.name} ${sex} ${this.age} 岁')
    }
}
main() {
    let p1 = Person('无名')
    p1.name = '张三'  //variable 'p1' is immutable
    // 实例化的变量如果需要赋值修改,也需要用var定义
    var person = Person('张三')
    person.show()  //张三 男 18 岁
    person.age = 20
    person.name = '李四'
    person.show()  //李四 男 20 岁
    println(Person.sex) //无法访问字段 “sex”
}

mut 函数

mut 函数的定义

mut 函数与普通的实例成员函数相比,多一个 mut 关键字来修饰。
在函数之前增加 mut 修饰符之后,即可在函数体内修改成员变量的值。

struct Point{
    public Point(var x:Int64,var y:Int64){
    }
    func show(){
        println('坐标为(${x},${y})')
    }
    // func setX(x:Int64){ //实例成员变量 “x” 无法在不可变函数中修改
    //     this.x =x 
    // }
    mut func setY(y:Int64){
        this.y=y
    }
    func copy(){
        return this // this变量是当前实例的拷贝
    }
}
main() {
    var point = Point(3,4)
    var p2 = point.copy()
    point.show() //坐标为(3,4)
    point.setY(6)
    point.show() //坐标为(3,6)
    p2.show()  //坐标为(3,4)
}

mut 函数中的 this 不能作为表达式
mut 函数中的 lambda 或嵌套函数不能对 struct 的实例成员变量进行捕获。

struct Point{
    public Point(var x:Int64,var y:Int64){
    }
    func copy(){
        return this
    }
    // mut func copy2(){ 
    //     return this //在可变函数 “copy2” 中,“this” 不能用作表达式
    // } 
    mut func setX(a:Int64){
       this.x ={ =>a }()
       this.x ={=>a+this.x }() //'this' cannot be captured in the mutable function 'setX'
    }
}

接口中的 mut 函数

struct 类型在实现 interface 的函数时必须保持一样的 mut 修饰。struct 以外的类型实现 interface 的函数时不能使用 mut 修饰。

interface I {
    mut func f1(): Unit
    func f2(): Unit
}
struct A <: I {
    public mut func f1(): Unit {} // 正确使用
    public func f2(): Unit {} //正确使用
}
// struct B <: I {
//     public func f1(): Unit {} // Error,在接口中,“f1” 用 “mut” 修饰,但在结构体中没有
//     public mut func f2(): Unit {} // Error, 在接口中,'f2' 没有用 'mut' 修饰,但在结构体中却用了
// }
// class C <: I {
//     mut func f1(): Unit {} // Error,类体中函数声明上出现意外的修饰符 “mut”
//     func f2(): Unit {} // Error, “f2” 的可见性为 “internal”
// }
class D <: I {
    public func f1(): Unit {}//正确使用
    public func f2(): Unit {} //正确使用
}

当 struct 的实例赋值给 interface 类型时是拷贝语义,因此 interface 的 mut 函数并不能修改 struct 实例的值。

interface In {
    mut func f(): Unit
}
struct Foo <: In {
    public var v = 0
    public mut func f(): Unit {
        v += 1
    }
}
main() {
    var a = Foo()
    var b: In = a  
    b.f()  // Calling 'f' via 'b' cannot modify the value of 'a'
    println(a.v) // 0
    a.f()
    println(a.v)  //1
    var c:In = Foo()
    c.f()
    // println(c.v) //error: 'v' is not a member of interface 'In'
}

mut 函数的使用限制

1.因为 struct 是值类型,所以如果一个变量是 struct 类型且使用 let 声明,那么不能通过这个变量访问该类型的 mut 函数
2.如果一个变量的类型是 struct 类型,那么这个变量不能将该类型使用 mut 修饰的函数作为一等公民来使用,只能调用这些 mut 函数。
3.非 mut 的实例成员函数(包括 lambda 表达式)不能直接访问所在类型的 mut 函数,反之可以。

struct People{
    mut func fun1(){
        println('fun1')
        fun2() // 可以访问
    }
    func fun2(){
        println('fun2')
        fun1()  //错误:不可变函数 “fun2” 无法访问可变函数 “fun1”
    }
}
main() {
    var p1 = People()
    p1.fun1() // fun1
    let p2 = People()
    // p2.fun1()  //错误:不能在不可变值上使用可变函数
    // var f = p1.fun1 //错误:可变函数 “fun1” 不能单独作为引用使用
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容