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

仓颉编程(10)类的定义

一、类的基本概念与定义语法

1.1 类的基本概念

在仓颉编程语言中,类(class)是面向对象编程的核心概念,用于创建具有相同属性和行为的对象。与结构体(struct)相比,类具有以下重要特征:

类是引用类型,在赋值或传参时传递的是引用而非值,多个变量可以指向同一个对象。这意味着当你将一个类实例赋值给另一个变量时,实际上是在创建一个指向同一对象的新引用。相比之下,结构体是值类型,在赋值或传参时会进行值拷贝。

类支持继承机制,可以创建继承层次结构,而结构体不能继承。这使得类在实现代码复用和多态性方面具有更大的灵活性。每个类都是Object 类的子类(直接或间接),所有的仓颉类型(包括 Object)都隐式地实现 Any 接口。

1.2 类的定义语法

在仓颉中,类的定义以关键字 class 开头,后跟类名,然后是一对花括号包围的类定义体。类定义体中可以包含成员变量、成员函数、构造函数、静态初始化器等内容。

class 类名 {// 成员变量let/var 变量名: 类型 = 初始值// 构造函数public init(参数列表) {// 初始化逻辑}// 成员函数public func 函数名(参数列表): 返回类型 {// 函数体}
}

需要特别注意的是,class 只能定义在源文件顶层,不能在函数或其他类型内部定义类。这是仓颉语言的一个重要限制,确保了程序结构的清晰性。

1.3 类的基本示例

让我们通过一个简单的Rectangle(矩形)类来理解类的基本定义:

class Rectangle {let width: Int64let height: Int64public init(width: Int64, height: Int64) {this.width = widththis.height = height}public func area(): Int64 {width * height}
}

这个例子展示了类的基本结构:

  • 使用class关键字定义类
  • 包含两个成员变量width和height(类型为 Int64)
  • 定义了一个构造函数init,用于初始化成员变量
  • 包含一个成员函数area(),用于计算矩形面积

在构造函数中,我们使用this 关键字来引用当前对象的成员变量,避免与参数名冲突。this 关键字在类的成员函数中代表当前对象的引用。

1.4 类名的命名规范

在仓颉编程中,类名应遵循以下命名规范:

  • 使用大写开头的驼峰命名法(PascalCase),例如:UserAccount、Rectangle
  • 类名应具有描述性,能够清晰表达类的功能或含义
  • 避免使用过于简短或含义模糊的名称

遵循良好的命名规范有助于提高代码的可读性和可维护性。

二、类的成员定义

2.1 成员变量的定义

类的成员变量分为实例成员变量静态成员变量两类。

实例成员变量属于类的实例(对象),每个对象都有自己独立的成员变量副本。实例成员变量的定义方式如下:

class Rectangle {// 带初始值的实例成员变量let width: Int64 = 10var height: Int64 = 20// 没有初始值的实例成员变量(必须标注类型)let x: Int64var y: Int64public init(x: Int64, y: Int64) {this.x = xthis.y = y}
}

在定义实例成员变量时,可以设置初始值,也可以不设置初值但必须标注类型。未初始化的成员变量必须在构造函数中完成初始化,否则会导致编译错误。

静态成员变量使用static修饰符声明,属于类本身而不是类的实例。静态成员变量必须有初始值,只能通过类名访问,不能通过对象访问:

class MathUtils {static let PI: Float64 = 3.14159265359static var count: Int64 = 0public init() {MathUtils.count += 1}
}

在上面的例子中,PI是一个静态常量,count是一个静态变量,用于记录创建的 MathUtils 对象数量。

2.2 成员函数的定义

类的成员函数分为实例成员函数静态成员函数两类。

实例成员函数通过对象调用,可以访问实例成员和静态成员:

class Rectangle {let width: Int64let height: Int64public init(width: Int64, height: Int64) {this.width = widththis.height = height}public func area(): Int64 {return width * height}public func perimeter(): Int64 {return 2 * (width + height)}
}

在实例成员函数中,可以使用this关键字引用当前对象,访问实例成员变量和调用其他成员函数。

静态成员函数使用static修饰符声明,通过类名调用,只能访问静态成员,不能使用this关键字:

class MathUtils {static let PI: Float64 = 3.14159265359public static func circleArea(radius: Float64): Float64 {return MathUtils.PI * radius * radius}public static func circlePerimeter(radius: Float64): Float64 {return 2 * MathUtils.PI * radius}
}

静态成员函数的主要特点是不能访问实例成员,因为它们属于类本身而非特定实例。

2.3 访问修饰符

仓颉提供了四种访问修饰符来控制类成员的可见性:

修饰符

文件内

包及子包内

模块内

所有包

private

internal

protected

public

private:仅在当前文件内可见。不同文件无法访问这类成员。

internal:在当前包及子包内可见(默认修饰符)。同一包内可以不导入就访问,子包内可以通过导入访问。

protected:在当前模块内及子类可见。同一包的文件可以不导入就访问,不同包但在同一模块内的其它包可以通过导入访问。

public:模块内外均可见。任何地方都可以访问,只要导入了相应的模块。

在类的定义中,如果没有显式指定访问修饰符,成员变量和成员函数的默认访问级别是internal。

2.4 成员变量的初始化方式

在仓颉中,类的成员变量可以通过多种方式初始化:

在定义时直接初始化

class Person {let name: String = "Unknown"var age: Int64 = 0static let DEFAULT_AGE: Int64 = 18
}

在构造函数中初始化

class Point {let x: Int64let y: Int64public init(x: Int64, y: Int64) {this.x = xthis.y = y}
}

使用静态初始化器初始化静态成员

class Configuration {static let APP_NAME: Stringstatic let VERSION: Stringstatic let BUILD_NUMBER: Int64static init() {APP_NAME = "MyApp"VERSION = "1.0.0"BUILD_NUMBER = 100}
}

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

三、类的实例化与对象创建

3.1 构造函数的定义与使用

在仓颉中,类支持两种构造函数:普通构造函数主构造函数

普通构造函数以init关键字开头,后跟参数列表和函数体。普通构造函数必须完成所有未初始化实例成员变量的初始化,否则会导致编译错误:

class Rectangle {let width: Int64let height: Int64// 普通构造函数public init(width: Int64, height: Int64) {this.width = width  // 正确:初始化width// this.height = height  // 错误:未初始化height}
}

上面的例子会产生编译错误,因为height成员变量未被初始化。

主构造函数的函数名与类名相同,最多只能定义一个。主构造函数的参数列表中可以使用let或var修饰符,这些参数会自动成为类的成员变量:

class Point {// 主构造函数public Point(let x: Int64, let y: Int64) {// 无需手动初始化x和y}// 普通构造函数(可选)public init() {this.x = 0this.y = 0}
}

在主构造函数中,参数前加let或var可以同时定义成员变量。这种方式简化了代码,避免了在构造函数体中重复编写初始化代码。

3.2 构造函数的重载

仓颉支持构造函数重载,允许在一个类中定义多个构造函数,但它们的参数列表必须不同(参数个数、类型或顺序不同):

class Rectangle {let width: Int64let height: Int64// 构造函数1:指定宽和高public init(width: Int64, height: Int64) {this.width = widththis.height = height}// 构造函数2:正方形(宽高相等)public init(side: Int64) {this.width = sidethis.height = side}// 构造函数3:默认构造函数public init() {this.width = 0this.height = 0}
}

构造函数重载的判断规则包括:

  • 参数个数不同
  • 参数类型不同
  • 参数顺序不同(仅适用于命名参数)

3.3 对象的创建与初始化

创建类的实例(对象)通过调用构造函数实现,语法格式为:

let/var 变量名 = 类名(构造函数参数)

例如,创建 Rectangle 类的实例:

// 创建一个宽10、高20的矩形
let rect1 = Rectangle(width: 10, height: 20)
// 创建一个边长为5的正方形(使用side参数的构造函数)
let rect2 = Rectangle(side: 5)
// 创建一个默认的矩形(使用无参构造函数)
let rect3 = Rectangle()

在创建对象时,根据传递的参数类型和数量,编译器会自动匹配相应的构造函数。

3.4 构造函数的执行顺序

在继承关系中,构造函数的执行遵循特定顺序:

  1. 初始化有缺省值的变量:首先初始化类中所有在定义时就设置了默认值的成员变量。
  2. 调用父类构造函数:如果子类构造函数没有显式调用父类构造函数,编译器会自动插入super()调用。
  3. 执行构造函数体内的代码:最后执行当前构造函数体中的代码。

以下是一个演示构造函数执行顺序的例子:

open class Animal {var name: String = "Animal"init() {println("Animal constructor: name = \(name)")}
}
class Dog <: Animal {var breed: String = "Unknown"init() {// 隐式调用super()breed = "Golden Retriever"println("Dog constructor: breed = \(breed)")}
}
main() {let dog = Dog()
}

输出结果:

Animal constructor: name = Animal
Dog constructor: breed = Golden Retriever

从输出可以看出,父类 Animal 的构造函数先执行,然后才是子类 Dog 的构造函数。

3.5 析构函数(终结器)

在仓颉中,析构函数称为终结器(Finalizer),用于在对象被垃圾回收前执行清理操作。终结器的特点如下:

  • 函数名固定为~init
  • 没有参数、返回类型和修饰符
  • 不能被显式调用,由垃圾回收器自动触发
  • 执行时机不确定,可能在任意线程上执行

终结器的使用示例:

import std.core.LibC
class FileHandler {var filePath: Stringvar fileHandle: CStringinit(path: String) {filePath = pathfileHandle = unsafe { LibC.mallocCString(path) }}~init() {unsafe { LibC.free(fileHandle) }println("File \(filePath) closed")}
}

需要注意的是,只有非 open 修饰的 class 才能定义终结器。终结器主要用于释放系统资源(如文件句柄、网络连接等),不建议在终结器中执行复杂操作。

http://www.dtcms.com/a/511233.html

相关文章:

  • 常见的域名注册网站报纸做垂直门户网站
  • 东营网站建设推广市政工程中标查询网
  • Llinux自动安装chrome与chromedriver
  • 低侧单向电流检测电路
  • 一款轻量级 Java CLI 工具,用于抓取、展示和导出 Exploit-DB 的漏洞数据
  • 运维效率翻倍:如何利用阿里云监控工具实现服务器智能运维?
  • [人工智能-大模型-29]:大模型应用层技术栈 - 第二层:Prompt 编排层(Prompt Orchestration)
  • 告别笔记局限!Blinko+cpolar让AI笔记随时随地可用
  • 【多线程】可重入锁 Reentrant Lock
  • 蓝牙低功耗(BLE)通信的中心设备/外围设备(连接角色)、主机/从机(时序角色)、客户端/服务器(数据交互角色)的理解
  • 3.5 面向连接的传输: TCP
  • 深度学习(10)-PyTorch 卷积神经网络
  • 网站没有做实名认证推广员是干什么的
  • 异步的feign请求报错:No thread-bound request found
  • 北京建设公司网站建设重庆有网站公司
  • YUV实战案例:一个网络摄像头的工作流程(速通)
  • 深入解析SCT分散加载文件
  • AIGC-Fooocus部署实践:从本地手动配置到云端一键启用的深度剖析
  • 数据结构——最小(代价)生成树
  • NumPy的hstack函数详细教程
  • 020数据结构之优先队列——算法备赛
  • 华为OD-23届考研-测试面经
  • 阿里云网站建设步骤wordpress防止频繁搜索
  • 西宁网站建设哪家公司好东莞seo网站推广
  • 2025年AI IDE的深度评测与推荐:从单一功能效率转向生态壁垒
  • OSS存储的视频,安卓和PC端浏览器打开正常,苹果端打开不播放,什么原因?
  • Spark的shuffle类型与对比
  • 【 论文精读】VIDM:基于扩散模型的视频生成新范式
  • CentOS 7 安装指定内核版本与切换内核版本
  • Spring MVC 拦截器interceptor