d的两种初化方式不一样

原文

struct A
{
    int[] i;
}
struct B
{
    A[] a = [A.init];
}

A和B如上,代码:

auto b1 = B.init;
b1.a[0].i ~= 1;
b1.a ~= A.init;
b1.a[0].i ~= 11;
b1.a[1].i ~= 12;
b1.writeln;

auto b2 = B();
b2.writeln;
//期望相同
B([A([1, 11]), A([12])])
B([A([])])

期望编译.但代码:

B b1;      // auto b1 = B.init;
b1.a[0].i ~= 1;
b1.a ~= A.init;
b1.a[0].i ~= 11;
b1.a[1].i ~= 12;
b1.writeln;

B b2;     // auto b2 = B();
b2.writeln;
//没有打印期望的
B([A([1, 11]), A([12])])
B([A([1])])

为什么?

A[] a = [A.init];

问题在此,它是引用在B的所有副本中共享的静态数组实例.几乎肯定你不想要它.
B.a[0]是跨不同默认构造的所有B*相同对象*.
不要这样.只有基本值不变串才这样初化.用数组或类对象,可能得到共享.
如果在构造器(需要个参数)或工厂函数中初化,会获得更加一致行为,因为每个实例都有自己的数组.
至于为什么B()B.init在此不同,我不知道,可能是编译器一些微妙实现.

我最近实验:

struct Xml {
    string beg;
    string end = "/>";  // 所有实例共享
    // 无分配.
// ...
}

这不是错误:

void main()
{
  struct B
  {
    struct A
    {
      int i = 10;
    }
    A[] a = [A.init];
  }

  B[2] b;
  assert(b[0].a.ptr is b[1].a.ptr);
}

这不是错误.他们指向内存中A的完全相同实例:
这里是:编译时分配了单个全局的在所有B.init实例之间共享的A[].就像如下:

struct B
{
    struct A
    {
        int i = 10;
    }
    static A[] globalArray = [A.init];
    A[] a = globalArray;
}

我想调整默认构造器,但不能有自定义版.
添加带参构造器,似乎是个侵改.

有静态opCall,也像侵改.

import std.stdio;

struct A
{
    int[] i;
}
struct B
{
    A[] a = [A.init];

    static B opCall()
    {//
        return B.init;
    }
}

void main()
{
    auto b1 = B.init;
    b1.writeln;

    B b2 = B();
    b2.writeln;

    B b3;
    b3.writeln;
}

静态数组是值类型.复制静态数组时,副本数据存储在与原始数组不同的内存块中:

int[1] a = [1];
int[1] b = a;

assert(&a[0] !is &b[0]);
//内存不同

动态数组引用类型.复制动态数组时,两个副本都指向同一个内存块:

int[] a = [1];
int[] b = a;

assert(&a[0] is &b[0]);

为创建有自己内存块的与原始数组分开的动态数组副本,必须使用内置.dup方法:

int[] a = [1];
int[] b = a.dup;

assert(&a[0] !is &b[0]);
//不同内存
posted @   zjh6  阅读(11)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示