跳转到内容

更多 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
华夏公益教科书