Help us understand the problem. What is going on with this article?

deleteがなくなる話

More than 1 year has passed since last update.

はじめに

以前から「そのうち消すから使わないでね」と言われていたが、いつまでたっても消えないので、「このまま残ったままになるのでは・・・」と思われていたdelete演算子ですが、dmd2.079.0(2018年3月1日リリース)でついに廃止予定となりました。一定期間の後言語から削除される予定です。

(ちなみにdmd2.080.0で、カスタムアロケータ(クラスに独自のnew/deleteを定義するやつ)もひっそりと廃止予定となりました。)

変更からだいぶ時間が経っているのですが、ネタがない気付いたのが最近だったのと一応年内の出来事なので、今さらですが書いてみることにしました。

代替の機能

以下はdeleteを使用した際に表示される警告メッセージです。

Deprecation: The `delete` keyword has been deprecated.
Use `object.destroy()`
 (and `core.memory.GC.free()` if applicable) instead.

代替機能としてobject.destroy()が案内されています。

deleteの動作は大まかには次の通りです。

  1. オブジェクトの破棄(デストラクタ)
  2. メモリの解放
  3. 参照変数の初期化(nullの代入)

このうち1.を行うのがobject.destroy()であり、2.を行うのがcore.memory.GC.free()に相当します。
元の挙動がどうしても必要な場合、core.memory.__delete()を使用することもできます。

D言語的にはメモリの解放はGCに任せてほしい、という感じでしょうか。


object.destroy()の動作(クラスの場合)は

  1. 初回のみ以下の処理を実行する
  2. メンバ変数、および本体のデストラクタを再帰的に呼び出す
  3. メンバ変数を初期状態に戻す

という感じで、object.destroy()後にオブジェクトに触っても(ある程度)安全なようになっています。

int dtorCount;
class Hoge {
    string s = "XXXX";
    this() { s = "Hoge"; }
    ~this() { ++dtorCount; }
}
void main() {
    auto p = new Hoge();
    assert(dtorCount == 0);
    assert(p.s == "Hoge");
    .destroy(p);            // 1回目は実行される
    assert(dtorCount == 1);
    assert(p.s == "XXXX");  // 初期状態に戻っている
    .destroy(p);            // 2回目以降は何もしない
    assert(dtorCount == 1);
    assert(p.s == "XXXX");
}

注意点として、object.destroy()はGCにはノータッチ(GCを実行したりメモリを解放したりしない)とドキュメントには書いてあるのですが、現時点では@nogcではないようです(object.destroy()の問題ではなく、クラスのデストラクタの問題)。

class Hoge {
    void print(string s) @nogc {
        import core.stdc.stdio;
        fprintf(stdout, "%s", s.ptr);
    }
}
void main() @nogc {
    import core.stdc.stdlib, std.conv;
    // GCを使わずに、malloc, freeでメモリ確保
    enum size = __traits(classInstanceSize, Hoge);
    void* mem = malloc(size);
    scope(exit) free(mem);
    // emplaceでインスタンスを割り当て
    auto hoge = emplace!Hoge(mem[0 .. size]);
    // 普通に使用できる
    hoge.print("hello");

    .destroy(hoge);  // Error: `@nogc` function `D main` cannot call non-@nogc function `object.destroy!(true, Hoge).destroy`
}
class Hoge { }
void main() @nogc {
    import std.experimental.allocator;
    import std.experimental.allocator.mallocator;
    auto p = Mallocator.instance.make!(Hoge)();
    // 同じ理由でこれもダメ
    Mallocator.instance.dispose(p);  // Error: `@nogc` function `D main` cannot call non-@nogc function `std.experimental.allocator.dispose!(shared(Mallocator), Hoge).dispose`
}

すぐには修正が難しい問題らしく、ちょっと残念。

廃止の理由

公式サイトのDeprecated Features(廃止予定の言語機能についてまとめたページ)のdeleteの項には

delete makes assumptions about the type of garbage collector available that limits which implementations can be used, and can be replaced by a library solution.

deleteが存在することで、D言語で使用可能なGCの種類が制限され、またライブラリ実装で置き換えが可能なため、とあります。
(※ちなみにInternetArchiveによると、2012年7月2日には既にこの記載がありました・・・。)

これだけだと、deleteは標準のGC実装に依存しているので、引き剥がすことが目的という風にも読めます。

ここがダメだよdelete

deleteの問題点として思いついたものを書いてみます。

  • オブジェクトへの参照が複数ある場合、delete後も解放済みのメモリにアクセス可能になる
import std.stdio;
class Hoge {
    string s_ = "XXXX";
    this(string s) { s_ = s; }
    ~this() { "%s.dtor()".writefln(s_); }
}
void main() {
    /// gc
    {
        scope a1 = new Hoge("a");  // オブジェクトを参照するa1とa2が消えてから
        scope a2 = a1;             // GCによってファイナライズされるので安全
    }
    "---".writeln();

    /// delete
    {
        scope b1 = new Hoge("b");
        scope b2 = b1;
        delete b1;                 // オブジェクトは削除され、b1はnullになるが
        b2.__dtor();               // b2経由で解放済みのメモリにアクセス可能であり危険
    }
}
a.dtor()
---
b.dtor()
XXXX.dtor()
  • const/immutableな変数の場合、deleteでは最後のnullの代入ができずにエラーになる(それはそう)
class Hoge { }
void main() {
    import core.memory;
    const p = new Hoge();
    delete p;  // Error: cannot modify `const` expression `p`
    .destroy(p);  // Ok
    GC.free(cast(void*)p);  // Ok
}

おわりに

今年はあまりD言語の情報を追えていなかったのですが、地味に色々と進化していて面白かったです。

nak2yoshi
D言語勉強中/ネコが好き
https://twitter.com/nak2yoshi
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away