Skip to content

复制与借用 ​

默认是复制 ​

在 cpro 里,赋值和传参默认都是复制:

cpro
bl a = "hello"
bl b = a            // b 是 a 的独立副本
b = "world"
dayin(a)            // hello,a 不受影响
cpro
变量 a = "hello"
变量 b = a          // b 是 a 的独立副本
b = "world"
打印(a)             // hello,a 不受影响
cpro
var a = "hello"
var b = a           // b 是 a 的独立副本
b = "world"
print(a)            // hello,a 不受影响

字符串、数组、结构体都一样,复制出来的值和原值互不影响。变量离开作用域时,它占用的内存会自动释放,不需要手动管理,也没有垃圾回收。

复制是安全的,但字符串、数组这类值复制一次要分配内存、拷贝数据。不想复制时有三种做法:

想干什么怎么写
让函数读一下、改一下,用完还回来借用 &(本页)
把值彻底交出去,自己不再用yidong(移动 / move)
值不用了,提前释放内存xiaohui(销毁 / destroy)

借用 ​

在形参类型前加 &,函数就借用调用方的变量:不复制,直接读写原来那一份。调用时实参前也要写 &:

cpro
hs show(s &zifuchuan) kongbai {
    dayin(s)              // 直接当 zifuchuan 用
}

rukou() {
    bl a = "hello"
    show(&a)              // 把 a 借出去,不复制
    dayin(a)              // 用完还回来,a 照常可用
}
cpro
函数 show(s &string) void {
    打印(s)               // 直接当 string 用
}

入口() {
    变量 a = "hello"
    show(&a)              // 把 a 借出去,不复制
    打印(a)               // 用完还回来,a 照常可用
}
cpro
fn show(s &string) void {
    print(s)              // 直接当 string 用
}

rukou() {
    var a = "hello"
    show(&a)              // 把 a 借出去,不复制
    print(a)              // 用完还回来,a 照常可用
}

两边都要写 &:声明写了、调用没写会报错,反过来也一样。

借用能读也能改 ​

借用参数用起来和普通值一样,不需要额外的语法。修改它,改的就是调用方的变量:

cpro
jiegouti Point {
    x leixing z64,
    y leixing z64,
}

hs move_right(p &Point) kongbai {
    p.x = p.x + 100
}

rukou() {
    bl pt leixing Point = { x: 1, y: 2 }
    move_right(&pt)
    dayin(pt.x)           // 101,pt 被改了
}
cpro
结构体 Point {
    x 类型 i64,
    y 类型 i64,
}

函数 move_right(p &Point) void {
    p.x = p.x + 100
}

入口() {
    变量 pt 类型 Point = { x: 1, y: 2 }
    move_right(&pt)
    打印(pt.x)            // 101,pt 被改了
}
cpro
struct Point {
    x type i64,
    y type i64,
}

fn move_right(p &Point) void {
    p.x = p.x + 100
}

rukou() {
    var pt type Point = { x: 1, y: 2 }
    move_right(&pt)
    print(pt.x)           // 101,pt 被改了
}

数组同理,在借用参数上 zhuijia 会加到调用方的数组里:

cpro
hs grow(xs &[z64]) kongbai {
    xs.zhuijia(7)
}

rukou() {
    bl a leixing [z64] = [1]
    grow(&a)
    dayin(a.changdu())    // 2
}
cpro
函数 grow(xs &[i64]) void {
    xs.push(7)
}

入口() {
    变量 a 类型 [i64] = [1]
    grow(&a)
    打印(a.length())      // 2
}
cpro
fn grow(xs &[i64]) void {
    xs.push(7)
}

rukou() {
    var a type [i64] = [1]
    grow(&a)
    print(a.length())     // 2
}

能借什么 ​

只能借有名字的东西:变量、结构体字段、数组元素。

cpro
f(&a)                     // ✓ 变量
f(&a.name)                // ✓ 字段
f(&arr[i])                // ✓ 数组元素
f(&(a + b))               // ✗ 不能借临时值
cpro
f(&a)                     // ✓ 变量
f(&a.name)                // ✓ 字段
f(&arr[i])                // ✓ 数组元素
f(&(a + b))               // ✗ 不能借临时值
cpro
f(&a)                     // ✓ 变量
f(&a.name)                // ✓ 字段
f(&arr[i])                // ✓ 数组元素
f(&(a + b))               // ✗ 不能借临时值

规则 ​

& 只能用在函数参数上,不能作变量类型、返回类型、结构体字段或数组元素:

cpro
hs f(x &z32) kongbai { }      // ✓
bl x leixing &z32             // ✗
hs g() &z32 { }               // ✗
cpro
函数 f(x &i32) void { }       // ✓
变量 x 类型 &i32              // ✗
函数 g() &i32 { }             // ✗
cpro
fn f(x &i32) void { }         // ✓
var x type &i32               // ✗
fn g() &i32 { }               // ✗

这条限制保证借用不会比原变量活得更久,所以不会出现"原变量已经释放、借用还在用"的情况。

不能借只读变量,因为借用可以修改它:

cpro
bl({ readonly: zheng }) a = 10
f(&a)                     // ✗
cpro
变量({ readonly: 真 }) a = 10
f(&a)                     // ✗
cpro
var({ readonly: true }) a = 10
f(&a)                     // ✗

不能借"借用"。借用参数要交给另一个函数时,只能让那个函数按值接收(会复制一份):

cpro
hs f(x &z32) kongbai {
    g(&x)                 // ✗ x 本身已经是借用
    h(x)                  // ✓ h 按值接收,复制一份
}
cpro
函数 f(x &i32) void {
    g(&x)                 // ✗ x 本身已经是借用
    h(x)                  // ✓ h 按值接收,复制一份
}
cpro
fn f(x &i32) void {
    g(&x)                 // ✗ x 本身已经是借用
    h(x)                  // ✓ h 按值接收,复制一份
}

编译器不会替你决定 ​

要不复制就自己写 &。即使函数里没有修改参数,编译器也不会自动改成借用——参数是复制还是借用,只看你写了什么,读代码就能确定。

传临时值本来就不复制,例如 show(greet()) 直接把 greet() 的结果交给 show,只有传变量时才会复制。

cpro 目前处于测试阶段