跳轉到內容

更多 C++ 慣用法/執行環繞指標

來自 Wikibooks,開放書籍,開放世界

執行環繞指標

[編輯 | 編輯原始碼]

提供一個智慧指標物件,它在對物件上的每個函式呼叫之前和之後透明地執行操作,前提是執行的操作對於所有函式都是相同的。[1] 這可以被視為面向切面的程式設計 (AOP) 的一種特殊形式。

也稱為

[編輯 | 編輯原始碼]

智慧指標或裝飾器的雙重應用。

通常需要在類的每個成員函式呼叫之前和之後執行一個功能。例如,在多執行緒應用程式中,需要在修改資料結構之前加鎖,並在之後解鎖。在資料結構視覺化應用程式中,可能希望在每次插入/刪除操作之後檢視資料結構的大小。

using namespace std;
class Visualizer {
    std::vector <int> & vect;
  public:
    Visualizer (vector<int> &v) : vect(v) {}
    void data_changed () {
       std::cout << "Now size is: " << vect.size();
    }
};
int main () // A data visualization application.
{
  std::vector <int> vector;
  Visualizer visu (vector);
  //...
  vector.push_back (10);
  visu.data_changed ();
  vector.push_back (20);
  visu.data_changed ();
  // Many more insert/remove calls here
  // and corresponding calls to visualizer.
}

這種函式呼叫的重複是容易出錯且乏味的。如果能夠自動呼叫視覺化器,那就太好了。視覺化器也可以用於 std::list <int>。這種不是單個類的一部分而是跨越多個類的功能通常被稱為方面。這個特殊的慣用法對於設計和實現簡單的方面非常有用。

解決方案和示例程式碼

[編輯 | 編輯原始碼]
class VisualizableVector {
  public:
    class proxy {
      public:
        proxy (vector<int> *v) : vect (v) {
          std::cout << "Before size is: " << vect->size ();
        }
        vector<int> * operator -> () {
          return vect;
        }
        ~proxy () {
          std::cout << "After size is: " << vect->size ();
        }
      private:
        vector <int> * vect;
    };
    VisualizableVector (vector<int> *v) : vect(v) {}    
    proxy operator -> () {
       return proxy (vect);
    }
  private:
    vector <int> * vect;
};
int main()
{
  vector<int> vec;
  VisualizableVector vecc(&vec);
  //...
  vecc->push_back(10); // Note use of -> operator instead of . operator
  vecc->push_back(20);
}

視覺化向量過載的 -> 運算子建立一個臨時代理物件並返回它。代理物件的建構函式記錄向量的尺寸。然後呼叫代理物件的過載 -> 運算子,它只是透過返回指向它的原始指標將呼叫轉發給底層向量物件。在對向量的實際呼叫完成後,代理物件的解構函式再次記錄尺寸。因此,視覺化的記錄是透明的,主函式免於雜亂。這種慣用法是執行環繞代理的特殊情況,執行環繞代理更通用和更強大。

如果我們明智地將這種慣用法與模板結合起來並連線過載的 -> 運算子,我們可以發揮它的真正威力。

template <class NextAspect, class Para>
class Aspect
{
  protected:
    Aspect (Para p): para_(p) {}
    Para  para_;
  public:
    NextAspect operator -> () 
    {
      return NextAspect (para_);
    }
};

template <class NextAspect, class Para>
struct Visualizing : Aspect<NextAspect, Para>
{
  public:
    Visualizing (Para p) 
       : Aspect<NextAspect, Para> (p) 
    {
	std::cout << "Before Visualization aspect" << std::endl;
    }
    ~Visualizing () 
    {
	std::cout << "After Visualization aspect" << std::endl;
    }
};
template <class NextAspect, class Para>
struct Locking : Aspect<NextAspect, Para>
{
  public:
    Locking (Para p) 
       : Aspect<NextAspect, Para> (p) 
    {
		std::cout << "Before Lock aspect" << std::endl;
    }
    ~Locking () 
    {
	std::cout << "After Lock aspect" << std::endl;
    }
};
template <class NextAspect, class Para>
struct Logging : Aspect<NextAspect, Para>
{
  public:
    Logging (Para p) 
        : Aspect <NextAspect, Para> (p) 
    {
		std::cout << "Before Log aspect" << std::endl;
    }
    ~Logging () 
    {
	std::cout << "After Log aspect" << std::endl;
    }
};
template <class Aspect, class Para>
class AspectWeaver 
{
public:
    AspectWeaver (Para p) : para_(p) {}    
    Aspect operator -> () 
    {
       return Aspect (para_);
    }
private:
	Para para_;
};

#define AW1(T,U) AspectWeaver<T<U, U>, U>
#define AW2(T,U,V) AspectWeaver<T<U<V, V>, V>, V>
#define AW3(T,U,V,X) AspectWeaver<T<U<V<X, X>, X>, X>, X>

int main()
{
  vector<int> vec;
  AW3(Visualizing, Locking, Logging, vector <int> *) X(&vec);
  //...
  X->push_back(10); // Note use of -> operator instead of . operator
  X->push_back(20);
}

已知用途

[編輯 | 編輯原始碼]
[編輯 | 編輯原始碼]

智慧指標

參考文獻

[編輯 | 編輯原始碼]
  1. 執行環繞序列 - Kevlin Henney
華夏公益教科書