2016年8月20日 星期六

cxxlObject 結束時要小心

CxxlMan2 程式庫的核心 cxxlObject 和 Smart_Ptr 保證了兩件事:
  1. cxxlObject 已沒有在被使用時,垃圾處理核心會自動銷毀。
  2. cxxlObject 若還在被使用就不會被銷毀,所以持有者可以放心使用,因至少還有一個持有者還在使用。

表面上看起來這兩段話蠻合情合理,但有一種情況,所持有的物件有可能不存在了,而且不影響以上兩點保證,那就是循環參照。持有者本身已不在被使用時,是否還有資格當持有者?會出現問題的地方在持有者的解構函數去叫用持有的 cxxlObject 物件,如以下的例子。

#include <iostream>
#include <SMART_PTR.HPP>

using namespace std;
using namespace CxxlMan2;

class B;

class A:virtual public cxxlObject
{
  Smart_Ptr<B> m_B_Ptr; // A 持有 B 物件

public:

  // Constructor
  A()
    :m_B_Ptr(nullptr,this)
  {}

  // Destructor 實作在後面
  ~A(); 
  
  friend class B;
};

class B :virtual public cxxlObject
{
  Smart_Ptr<A> m_A_Ptr; // B 持有 A 物件
  int i;

public:  
  // Constructor
  B()
    :m_A_Ptr(new A, this)
  {
    m_A_Ptr->m_B_Ptr = this;
  }

  void F1()
  {
    ++i;
    cout << "this is B::F1" << endl;
  }
};

// Destructor
A::~A()
{
  m_B_Ptr->F1(); // A 結束前叫用它所持有的 B 物件
}

int main()
{
  {
    // rootObject 持有 B 物件
    Smart_Ptr<B> B_Ptr(new B); 
  }

  // 等待所有的物件都結束
  Wait_cxxlObjectDeathQueue_Emptied(); 
  return 0;
}

當 main() 的 B_Ptr 結束後,A B 兩物件只剩互相持有,不再有實質的使用持有者,這時垃圾處理核心會結束掉這兩個物件,當 A 的解構函數去呼叫 B::F1() 有可能 B 物件已經不存在了(若 B 比較早被結束掉)。

因此在解構函數叫用持有的物件是有風險的,除非你能確定不會和持有的物件形成循環參照,解決的辦法是把解構函數的東西搬出來,做一個結束前的處理函數,在物件結束前先叫用



沒有留言:

張貼留言