5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

D言語Advent Calendar 2018

Day 13

deleteがなくなる話

Last updated at Posted at 2018-12-12

はじめに

以前から「そのうち消すから使わないでね」と言われていたが、いつまでたっても消えないので、「このまま残ったままになるのでは・・・」と思われていた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言語の情報を追えていなかったのですが、地味に色々と進化していて面白かったです。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?