引言
在仓颉语言中,结构体(Struct)是一种重要的复合数据类型,它允许我们将相关的数据和行为组织在一起。与类(Class)不同,结构体是值类型,具有复制语义,这使得它们在很多场景下更加高效和安全。
1. 结构体的基本概念
1.1 什么是结构体
结构体是仓颉语言中的一种复合数据类型,它以关键字 struct
开头,后跟结构体名称,然后是一对花括号包围的定义体。结构体定义体中可以包含:
- 成员变量(实例变量和静态变量)
- 成员属性(实例属性和静态属性)
- 静态初始化器
- 构造函数(主构造函数和普通构造函数)
- 成员函数(实例函数和静态函数)
重要限制:结构体只能定义在源文件的顶层作用域中。
1.2 基本结构体定义示例
cangjie
struct Rectangle {
let width: Int64
let height: Int64
public init(width: Int64, height: Int64) {
this.width = width
this.height = height
}
public func area() {
width * height
}
}
这个例子定义了一个名为 Rectangle
的结构体,包含:
- 两个
Int64
类型的成员变量width
和height
- 一个接受两个参数的构造函数
- 一个计算面积的成员函数
2. 结构体成员变量详解
2.1 实例成员变量
实例成员变量是结构体实例的组成部分,每个实例都有自己独立的成员变量副本。
cangjie
struct Person {
// 不设置初值,但必须标注类型
let name: String
let age: Int64
// 设置初值
let country = "China"
let isStudent = true
public init(name: String, age: Int64) {
this.name = name
this.age = age
}
}
2.2 静态成员变量
使用 static
修饰符修饰的成员变量属于结构体类型本身,而不是实例。
cangjie
struct MathConstants {
static let PI: Float64
static let E: Float64
static let GOLDEN_RATIO: Float64
static init() {
PI = 3.14159265359
E = 2.71828182846
GOLDEN_RATIO = 1.61803398875
}
}
访问方式:
- 实例成员变量:通过结构体实例访问(如
person.name
) - 静态成员变量:通过结构体类型名访问(如
MathConstants.PI
)
3. 静态初始化器
3.1 静态初始化器的定义
静态初始化器用于初始化静态成员变量,以 static init
开头,后跟无参参数列表和函数体。
cangjie
struct Configuration {
static let maxConnections: Int64
static let timeout: Int64
static let debugMode: Bool
static init() {
maxConnections = 100
timeout = 30
debugMode = false
}
}
3.2 重要规则
- 一个结构体中最多只能定义一个静态初始化器
- 静态初始化器不能被访问修饰符修饰
- 函数体中必须完成对所有未初始化的静态成员变量的初始化
错误示例:
cangjie
struct Rectangle {
static let degree: Int64
static init() {
degree = 180
}
static init() { // 编译错误:重复定义静态初始化器
degree = 180
}
}
4. 构造函数详解
4.1 普通构造函数
普通构造函数以关键字 init
开头,后跟参数列表和函数体。
cangjie
struct Point {
let x: Int64
let y: Int64
public init(x: Int64, y: Int64) {
this.x = x
this.y = y
}
public init() {
this.x = 0
this.y = 0
}
}
4.2 构造函数重载
仓颉语言支持构造函数重载,但必须确保参数列表不同:
cangjie
struct Circle {
let radius: Float64
let centerX: Float64
let centerY: Float64
public init(radius: Float64) {
this.radius = radius
this.centerX = 0.0
this.centerY = 0.0
// 也可以简便的直接调用其他构造函数
// this(radius, 0.0, 0.0)
}
public init(radius: Float64, centerX: Float64, centerY: Float64) {
this.radius = radius
this.centerX = centerX
this.centerY = centerY
}
}
错误示例:
cangjie
struct Rectangle {
let width: Int64
let height: Int64
public init(width: Int64) {
this.width = width
this.height = width
}
public init(height: Int64) { // 编译错误:与第一个构造函数重载冲突
this.width = height
this.height = height
}
}
4.3 主构造函数
主构造函数是结构体的一种特殊构造函数,名字与结构体类型名相同,最多只能定义一个。
cangjie
struct Student {
public Student(let name: String, let age: Int64, let grade: String) {}
}
主构造函数的参数列表中可以使用 let
或 var
修饰符来定义成员变量形参,这些参数同时扮演定义成员变量和构造函数参数的功能。
带普通参数的主构造函数:
cangjie
struct Employee {
public Employee(id: String, let name: String, let department: String) {}
}
4.4 自动生成的构造函数
如果结构体没有自定义构造函数,且所有实例成员变量都有初始值,编译器会自动生成一个无参构造函数:
cangjie
struct DefaultRectangle {
let width: Int64 = 10
let height: Int64 = 10
/* 自动生成的无参构造函数:
public init() {
}
*/
}
5. 结构体成员函数
5.1 实例成员函数
实例成员函数通过结构体实例调用,可以访问实例成员变量和静态成员变量。
cangjie
struct Calculator {
public Calculator(let baseValue: Int64) {}
// 被public修饰的函数,必须标注返回类型,否则编译错误
public func add(value: Int64): Int64 {
this.baseValue + value
}
public func multiply(value: Int64): Int64 {
this.baseValue * value
}
public func getBaseValue(): Int64 {
this.baseValue
}
}
5.2 静态成员函数
使用 static
修饰符修饰的成员函数属于结构体类型本身。
cangjie
struct StringUtils {
static func isEmpty(str: String): Bool {
str.isEmpty()
}
static func reverse(str: String): String {
// 实现字符串反转逻辑
str
}
}
5.3 函数中的this关键字
在实例成员函数中,可以通过 this
关键字访问实例成员变量:
cangjie
struct BankAccount {
var balance: Int64 = 0
// 如果需要修改成员变量的值,函数需要被mut修饰
public mut func deposit(amount: Int64): Unit {
this.balance = this.balance + amount
}
public mut func withdraw(amount: Int64): Bool {
if (this.balance >= amount) {
this.balance = this.balance - amount
true
} else {
false
}
}
}
6. 访问修饰符详解
6.1 四种访问级别
仓颉语言提供四种访问修饰符:
private
:仅在结构体定义内可见internal
:当前包及子包内可见(默认修饰符)protected
:当前模块可见public
:模块内外均可见
6.2 访问控制示例
cangjie
package cangjie_blog.a
public struct User {
public var username: String // 模块内外均可访问
var email: String // 默认internal,仅当前包及子包可见
private var password: String // 仅结构体内部可见
protected var lastLogin: Int64 // 仅当前模块可见
public init(username: String, email: String, password: String) {
this.username = username
this.email = email
this.password = password
this.lastLogin = 0
}
public func getEmail():String {
this.email
}
private func validatePassword(input: String): Bool {
input == this.password
}
}
同包内访问:
cangjie
package cangjie_blog.a
func samePackageFunction() {
var user = User("john", "john@example.com", "secret123")
user.username = "jane" // OK:public成员
user.email = "jane@example.com" // OK:internal成员
// user.password = "newpass" // 编译错误:private成员无法访问
user.lastLogin = 1234567890 // OK: protected成员可以被同模块访问
}
其他包访问:
cangjie
package cangjie_blog.b
import cangjie_blog.a.*
func otherPackageFunction() {
var user = User("john", "john@example.com", "secret123")
user.username = "jane" // OK:public成员
user.email = "jane@example.com" // 编译错误:internal成员无法访问
// user.password = "newpass" // 编译错误:private成员无法访问
user.lastLogin = 1234567890 // OK: protected成员可以被同模块访问
}
7. 创建结构体实例
7.1 基本实例化
通过调用构造函数创建结构体实例:
cangjie
package cangjie_blog
import std.math.sqrt
struct Point {
public var x: Int64
public var y: Int64
public init(x: Int64, y: Int64) {
this.x = x
this.y = y
}
public func distance(): Float64 {
sqrt(Float64(this.x * this.x + this.y * this.y))
}
}
main() {
let point = Point(3, 4)
let dist = point.distance() // 5.0
println("Distance: ${dist}")
}
7.2 可变实例
要修改结构体实例的成员变量,需要将变量声明为可变变量(使用 var
),且被修改的成员变量也必须是可变的:
cangjie
struct Counter {
public var count: Int64 = 0
// public 函数必须标注返回值类型
// 修改成员变量的函数必须被mut修饰
public mut func increment(): Unit {
this.count = this.count + 1
}
public mut func reset(): Unit {
this.count = 0
}
}
main(): Unit {
var counter = Counter() // 使用var声明可变变量
println("Initial count: ${counter.count}")
counter.increment()
println("After increment: ${counter.count}")
counter.count = 10 // 直接修改成员变量
println("After direct assignment: ${counter.count}")
counter.reset()
println("After reset: ${counter.count}")
}
7.3 值类型特性
结构体是值类型,赋值和传参时会进行复制:
cangjie
struct Color {
public var red: Int64
public var green: Int64
public var blue: Int64
public init(red: Int64, green: Int64, blue: Int64) {
this.red = red
this.green = green
this.blue = blue
}
public func isRed(): Bool {
this.red > this.green && this.red > this.blue
}
}
main() {
var color1 = Color(255, 100, 100)
var color2 = color1 // 复制color1的值给color2
println("Color1 is red: ${color1.isRed()}") // true
println("Color2 is red: ${color2.isRed()}") // true
color1.red = 100 // 修改color1
color1.green = 255
color1.blue = 100
println("After modification:")
println("Color1 is red: ${color1.isRed()}") // false
println("Color2 is red: ${color2.isRed()}") // true(不受影响)
}
8. Mut函数详解
8.1 为什么需要Mut函数
由于结构体是值类型,普通的实例成员函数无法修改实例本身。Mut函数是一种特殊的实例成员函数,可以修改结构体实例的成员变量。
普通函数无法修改成员变量:
cangjie
struct Counter {
var count = 0
public func increment(): Unit {
// count += 1 // 编译错误:无法在普通函数中修改实例成员变量
}
}
8.2 Mut函数定义
使用 mut
关键字修饰的实例成员函数:
cangjie
struct Counter {
var count = 0
public mut func increment(): Unit {
this.count = this.count + 1 // OK:mut函数可以修改成员变量
}
public mut func decrement(): Unit {
this.count = this.count - 1
}
public mut func reset(): Unit {
this.count = 0
}
public func getCount(): Int {
this.count
}
}
8.3 Mut函数的使用限制
8.3.1 静态函数不能使用Mut
cangjie
struct A {
public mut func f(): Unit {} // OK
public mut static func g(): Unit {} // 编译错误:静态函数不能使用mut修饰
}
8.3.2 Mut函数中的this限制
Mut函数中的 this
不能被捕获,也不能作为表达式:
cangjie
struct Foo {
var i = 0
public mut func f(): Foo {
// let f1 = { => this } // 编译错误:mut函数中的this不能被捕获
// let f2 = { => this.i = 2 } // 编译错误:实例成员变量不能被捕获
// let f3 = { => this.i } // 编译错误:实例成员变量不能被捕获
// let f4 = { => i } // 编译错误:实例成员变量不能被捕获
// this // 编译错误:mut函数中的this不能作为表达式
Foo() // 返回新实例
}
}
8.3.3 接口中的Mut函数
接口中的实例成员函数也可以使用 mut
修饰:
cangjie
interface Mutable {
mut func increment(): Unit
func getValue(): Int64
}
struct Counter <: Mutable {
var value = 0
public mut func increment(): Unit {
this.value = this.value + 1
}
public func getValue(): Int64 {
this.value
}
}
重要规则:
- 结构体实现接口时必须保持相同的
mut
修饰 - 类实现接口时不能使用
mut
修饰
cangjie
interface I {
mut func f1(): Unit
func f2(): Unit
}
struct A <: I {
public mut func f1(): Unit {} // OK:与接口保持一致
public func f2(): Unit {} // OK:与接口保持一致
}
class C <: I {
public func f1(): Unit {} // OK:类不需要mut修饰
public func f2(): Unit {} // OK
}
8.3.4 可变性要求
使用 let
声明的结构体变量无法调用 mut
函数:
cangjie
struct Counter {
var count = 0
public mut func increment(): Unit {
this.count = this.count + 1
}
}
main() {
let counter = Counter() // 使用let声明,不可变
// counter.increment() // 编译错误:let声明的变量无法调用mut函数
var mutableCounter = Counter() // 使用var声明,可变
mutableCounter.increment() // OK
}
8.3.5 函数调用限制
非 mut
函数不能直接调用 mut
函数:
cangjie
import std.collection.ArrayList
struct DataProcessor {
let data: ArrayList<Int64> = ArrayList()
public mut func addValue(value: Int64): Unit {
this.data.add(value)
this.processData() // OK:mut函数可以调用非mut函数
}
public func processData(): Unit {
// this.addValue(100) // 编译错误:非mut函数不能调用mut函数
println("Processing ${this.data.size} items")
}
}
9. 递归结构体限制
仓颉语言禁止递归和互递归定义的结构体:
cangjie
// 编译错误:递归引用自身
struct R1 {
let other: R1
}
// 编译错误:互递归定义
struct R2 {
let other: R3
}
struct R3 {
let other: R2
}
10. 实际应用示例
10.1 几何图形计算
cangjie
struct Circle {
public var radius: Float64
public var centerX: Float64
public var centerY: Float64
public init(radius: Float64, centerX: Float64, centerY: Float64) {
this.radius = radius
this.centerX = centerX
this.centerY = centerY
}
public func area(): Float64 {
MathConstants.PI * this.radius * this.radius
}
public func circumference(): Float64 {
2.0 * MathConstants.PI * this.radius
}
public func containsPoint(x: Float64, y: Float64): Bool {
let dx = x - this.centerX
let dy = y - this.centerY
(dx * dx + dy * dy) <= (this.radius * this.radius)
}
public mut func scale(factor: Float64): Unit {
this.radius = this.radius * factor
}
}
struct MathConstants {
static let PI: Float64 = 3.14159265359
}
10.2 银行账户管理
cangjie
import std.collection.ArrayList
struct BankAccount {
private var accountNumber: String
private var balance: Int64
private let transactions: ArrayList<String> = ArrayList()
public init(accountNumber: String, initialBalance: Int64) {
this.accountNumber = accountNumber
this.balance = initialBalance
this.addTransaction("Account opened with balance: ${initialBalance}")
}
public func getAccountNumber(): String {
this.accountNumber
}
public func getBalance(): Int64 {
this.balance
}
public func getTransactionHistory(): Array<String> {
this.transactions.toArray()
}
public mut func deposit(amount: Int64): Bool {
if (amount > 0) {
this.balance = this.balance + amount
this.addTransaction("Deposit: +${amount}")
true
} else {
false
}
}
public mut func withdraw(amount: Int64): Bool {
if (amount > 0 && this.balance >= amount) {
this.balance = this.balance - amount
this.addTransaction("Withdrawal: -${amount}")
true
} else {
false
}
}
private mut func addTransaction(description: String): Unit {
this.transactions.add(description)
}
}
10.3 学生信息管理
cangjie
import std.collection.{ArrayList, fold}
struct Student {
public var name: String
public var studentId: String
public let grades: ArrayList<Float64> = ArrayList()
public var isActive: Bool = true
public init(name: String, studentId: String) {
this.name = name
this.studentId = studentId
}
public func addGrade(grade: Float64): Unit {
if (grade >= 0.0 && grade <= 100.0) {
this.grades.add(grade)
}
}
public func getAverageGrade(): Float64 {
if (this.grades.size == 0) {
0.0
} else {
let sum = fold(0.0) {
acc, grade => acc + grade
}(this.grades)
sum / Float64(this.grades.size)
}
}
public func getGradeCount(): Int64 {
this.grades.size
}
public mut func deactivate(): Unit {
this.isActive = false
}
public mut func reactivate(): Unit {
this.isActive = true
}
public func getStatus(): String {
if (this.isActive) {
"Active"
} else {
"Inactive"
}
}
}
11. 最佳实践和注意事项
11.1 设计原则
- 单一职责:每个结构体应该只负责一个特定的功能领域
- 不可变性优先 :优先使用
let
声明成员变量,只在必要时使用var
- 封装性:使用适当的访问修饰符控制成员的可见性
- 值语义:充分利用结构体的值类型特性
11.2 性能考虑
- 避免大型结构体:大型结构体在复制时可能影响性能
- 合理使用静态成员:对于不依赖实例的数据,使用静态成员
- 谨慎使用Mut函数:只在确实需要修改实例状态时使用
11.3 常见陷阱
- 忘记初始化:确保所有成员变量在构造函数中被正确初始化
- 访问修饰符错误:注意不同包之间的访问权限
- Mut函数误用:理解何时需要使用Mut函数