インスタンス変数・クラス変数を定義する - lisp-cookbook-ja/common-lisp GitHub Wiki

オブジェクトシステム(CLOS)

インスタンス変数・クラス変数を定義する

インスタンス変数・クラス変数の違いは、defclassでのスロットの定義で :allocation に:classか:instanceを指定することにより定義します。 :allocation の指定が省略された場合は、:instanceの指定となります。

(defclass foo ()
  ((class-var :initform () 
              :allocation :class
              :accessor class-var)
   (instance-var :initform () 
                 :allocation :instance
                 :accessor instance-var)))

(let ((foo-1 (make-instance 'foo))
      (foo-2 (make-instance 'foo)))

  ;; foo-1の値を変更
  (setf (class-var foo-1) 100
        (instance-var foo-1) 50)

  ;; foo-2の値を確認
  (list (class-var foo-2)
        (instance-var foo-2)))
;=> (100 NIL)

クラス変数とした場合、継承した子クラスの同名スロットを変更すると親クラスのスロットまで変更されるので、これを意図しない場合注意が必要です。

(defclass bar (foo) ())

(let ((foo-1 (make-instance 'foo))      
      (bar-1 (make-instance 'bar)))

  ;; foo-1のclass-varに100を設定
  (setf (class-var foo-1) 100)

  ;; bar-1の値を変更
  (setf (class-var bar-1) 'bar
        (instance-var bar-1) 50)

  ;; foo-1の値を確認
  (list (class-var foo-1)
        (instance-var foo-1)))
;=> (BAR NIL)

上記を回避するには、子クラスで再度スロットの定義をし、インスタンス変数であることを明記する必要があります。

(defclass baz (foo) 
  (;; allocation指定を省略するとデフォルトは:instance
   (class-var :accessor class-var))) 

(let ((foo-1 (make-instance 'foo))      
      (baz-1 (make-instance 'baz)))

  ;; foo-1のclass-varに100を設定
  (setf (class-var foo-1) 100)

  ;; baz-1の値を変更
  (setf (class-var baz-1) 'baz
        (instance-var baz-1) 50)

  ;; foo-1の値を確認
  (list (class-var foo-1)
        (instance-var foo-1)))
;=> (100 NIL)