更多 C++ 习语/智能指针
外观
当句柄体 习语或信封信 习语正在使用时,减轻在句柄类中重复主体类的签名更改的负担。
- 整体委托
当使用句柄/主体习语时,可能需要在句柄类中重复主体类的接口,因为句柄由用户代码使用。这种重复通常很乏味且容易出错。智能指针习语用于减轻这种负担。智能指针习语通常与句柄类中的某种“智能”一起使用,例如引用计数、自动所有权管理等。
根据预期的用途,至少有两种实现智能指针习语的重叠方法。
- 完全指针语义
- 不太像指针的语义
以上两种变体都在所谓的“句柄”类中定义了一个重载的箭头运算符。让我们从完全指针语义开始。
class Body;
class Handle // Completely pointer like semantics
{
public:
void set (Body *b) { body_ = b; }
Body * operator -> () const throw()
{
return body_;
}
Body & operator * () const throw ()
{
return *body_;
}
private:
mutable Body *body_;
};
int main (void)
{
Handle h;
h.set(new Body());
h->foo(); // A way of invoking Body::foo()
(*h).foo(); // Another way of invoking Body::foo()
}
仅使用 -> 运算符可以减轻在句柄类中重复主体类接口的问题。另一种方法是像上面代码片段中显示的那样重载解引用 (*) 运算符,但它不像前者那样自然。如果句柄抽象是某种指针抽象,那么应该提供这两个重载运算符(例如,std::auto_ptr、boost::shared_ptr)。如果句柄抽象不是指针类抽象,则不需要提供 * 运算符。相反,为常量句柄提供重载的箭头运算符集可能很有用,因为客户端始终与句柄类对象交互。对于客户端代码,句柄**就是**对象,因此句柄的常量性应该在适当的时候传播到相应的主体。通常,应该避免从常量句柄中修改非常量主体对象的不明确行为。与纯粹的指针语义不同,在某些情况下,从句柄类到主体类的自动类型转换也是可取的。
class Body;
class Handle // Less pointer like semantics
{
public:
void set (Body *b) { body_ = b; }
Body * operator -> () throw()
{
return body_;
}
Body const * operator -> () const throw()
{
return body_;
}
operator const Body & () const // type conversion
{
return *body_;
}
operator Body & () // type conversion
{
return *body_;
}
// No operator *()
private:
mutable Body *body_;
};
int main (void)
{
Handle const h;
h.set(new Body());
h->foo(); // compiles only if Body::foo() is a const function.
}
使用成员转换函数的另一种方法是使用非成员获取 习语,如下所示。根据接口原则,重载的非成员 get() 函数必须与句柄类位于同一个命名空间中。
namespace H {
class Body;
class Handle { ... }; // As per above.
Body const & get (Handle const &h)
{
return *h.body_;
}
Body & get (Handle &h)
{
return *h.body_;
}
} // end namespace H.
int main (void)
{
H::Handle const h;
h.set(new Body());
get(h).foo(); // compiles only if Body::foo() is a const function.
}
- std::auto_ptr(完全指针语义)
- boost::shared_ptr(完全指针语义)
- C++ 中的 CORBA Var 类型(TAO(ACE ORB)中的 TAO_Seq_Var_Base_T< T > 类 - 不太像指针的语义)