⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 1057.html

📁 著名的linux英雄站点的文档打包
💻 HTML
📖 第 1 页 / 共 4 页
字号:
}<br>
  注意在Release调用以后,Smart Pointer就不再是对象的所有者了--它内部的指针指向空。<br>
现在,调用了Release都必须是一个负责的人并且迅速隐藏返回的指针到新的所有者对象中。在我们的例子中,容器调用了Release,比如这个Stack的例子:<br>
void Stack::Push (SPtr &lt;Item&gt; & item) throw (char *)<br>
{<br>
if (_top == maxStack)<br>
throw "Stack overflow";<br>
_arr [_top++] = item.Release ();<br>
};<br>
  同样的,你也可以再你的代码中用加强Release的可靠性。<br>
  相应的Pop方法要做些什么呢?他应该释放了资源并祈祷调用它的是一个负责的人而且立即作一个资源传递它到一个Smart Pointer?这听起来并不好。<br>
Strong Pointers<br>
  资源管理在内容索引(Windows NT Server上的一部分,现在是Windows 2000)上工作,并且,我对这十分满意。然后我开始想……这一方法是在这样一个完整的系统中形成的,如果可以把它内建入语言的本身岂不是一件非常好?我提出了强指针(Strong Pointer)和弱指针(Weak Pointer)。一个Strong Pointer会在许多地方和我们这个SPtr相似--它在超出它的作用域后会清除他所指向的对象。资源传递会以强指针赋值的形式进行。也可以有Weak Pointer存在,它们用来访问对象而不需要所有对象--比如可赋值的引用。<br>
  任何指针都必须声明为Strong或者Weak,并且语言应该来关注类型转换的规定。例如,你不可以将Weak Pointer传递到一个需要Strong Pointer的地方,但是相反却可以。Push方法可以接受一个Strong Pointer并且将它转移到Stack中的Strong Pointer的序列中。Pop方法将会返回一个Strong Pointer。把Strong Pointer的引入语言将会使垃圾回收成为历史。<br>
  这里还有一个小问题--修改C++标准几乎和竞选美国总统一样容易。当我将我的注意告诉给Bjarne Stroutrup的时候,他看我的眼神好像是我刚刚要向他借一千美元一样。<br>
  然后我突然想到一个念头。我可以自己实现Strong Pointers。毕竟,它们都很想Smart Pointers。给它们一个拷贝构造函数并重载赋值操作符并不是一个大问题。事实上,这正是标准库中的auto_ptr有的。重要的是对这些操作给出一个资源转移的语法,但是这也不是很难。<br>
template &lt;class T&gt;<br>
SPtr&lt;T&gt;::SPtr (SPtr&lt;T&gt; & ptr)<br>
{<br>
_p = ptr.Release ();<br>
}<br>
<br>
template &lt;class T&gt;<br>
void SPtr&lt;T&gt;::operator = (SPtr&lt;T&gt; & ptr)<br>
{<br>
if (_p != ptr._p)<br>
{<br>
delete _p;<br>
_p = ptr.Release ();<br>
}<br>
}<br>
  使这整个想法迅速成功的原因之一是我可以以值方式传递这种封装指针!我有了我的蛋糕,并且也可以吃了。看这个Stack的新的实现:<br>
class Stack<br>
{<br>
enum { maxStack = 3 };<br>
public:<br>
Stack ()<br>
: _top (0)<br>
{}<br>
void Push (SPtr&lt;Item&gt; & item) throw (char *)<br>
{<br>
if (_top &gt;= maxStack)<br>
throw "Stack overflow";<br>
_arr [_top++] = item;<br>
}<br>
SPtr&lt;Item&gt; Pop ()<br>
{<br>
if (_top == 0)<br>
return SPtr&lt;Item&gt; ();<br>
return _arr [--_top];<br>
}<br>
private<br>
int _top;<br>
SPtr&lt;Item&gt; _arr [maxStack];<br>
};<br>
  Pop方法强制客户将其返回值赋给一个Strong Pointer,SPtr&lt;Item&gt;。任何试图将他对一个普通指针的赋值都会产生一个编译期错误,因为类型不匹配。此外,因为Pop以值方式返回一个Strong Pointer(在Pop的声明时SPtr&lt;Item&gt;后面没有&符号),编译器在return时自动进行了一个资源转换。他调用了operator =来从数组中提取一个Item,拷贝构造函数将他传递给调用者。调用者最后拥有了指向Pop赋值的Strong Pointer指向的一个Item。<br>
我马上意识到我已经在某些东西之上了。我开始用了新的方法重写原来的代码。<br>
分析器(Parser)<br>
  我过去有一个老的算术操作分析器,是用老的资源管理的技术写的。分析器的作用是在分析树中生成节点,节点是动态分配的。例如分析器的Expression方法生成一个表达式节点。我没有时间用Strong Pointer去重写这个分析器。我令Expression、Term和Factor方法以传值的方式将Strong Pointer返回到Node中。看下面的Expression方法的实现:<br>
SPtr&lt;Node&gt; Parser::Expression()<br>
{<br>
// Parse a term<br>
SPtr&lt;Node&gt; pNode = Term ();<br>
EToken token = _scanner.Token();<br>
if ( token == tPlus || token == tMinus )<br>
{<br>
// Expr := Term { ('+' | '-') Term }<br>
SPtr&lt;MultiNode&gt; pMultiNode = new SumNode (pNode);<br>
do<br>
{<br>
_scanner.Accept();<br>
SPtr&lt;Node&gt; pRight = Term ();<br>
pMultiNode-&gt;AddChild (pRight, (token == tPlus));<br>
token = _scanner.Token();<br>
} while (token == tPlus || token == tMinus);<br>
pNode = up_cast&lt;Node, MultiNode&gt; (pMultiNode);<br>
}<br>
// otherwise Expr := Term<br>
return pNode; // by value!<br>
}<br>
  最开始,Term方法被调用。他传值返回一个指向Node的Strong Pointer并且立刻把它保存到我们自己的Strong Pointer,pNode中。如果下一个符号不是加号或者减号,我们就简单的把这个SPtr以值返回,这样就释放了Node的所有权。另外一方面,如果下一个符号是加号或者减号,我们创建一个新的SumMode并且立刻(直接传递)将它储存到MultiNode的一个Strong Pointer中。这里,SumNode是从MultiMode中继承而来的,而MulitNode是从Node继承而来的。原来的Node的所有权转给了SumNode。<br>
  只要是他们在被加号和减号分开的时候,我们就不断的创建terms,我们将这些term转移到我们的MultiNode中,同时MultiNode得到了所有权。最后,我们将指向MultiNode的Strong Pointer向上映射为指向Mode的Strong Pointer,并且将他返回调用着。<br>
  我们需要对Strong Pointers进行显式的向上映射,即使指针是被隐式的封装。例如,一个MultiNode是一个Node,但是相同的is-a关系在SPtr&lt; MultiNode&gt;和SPtr&lt;Node&gt;之间并不存在,因为它们是分离的类(模板实例)并不存在继承关系。up-cast模板是像下面这样定义的:<br>
template&lt;class To, class From&gt;<br>
inline SPtr&lt;To&gt; up_cast (SPtr&lt;From&gt; & from)<br>
{<br>
return SPtr&lt;To&gt; (from.Release ());<br>
}<br>
  如果你的编译器支持新加入标准的成员模板(member template)的话,你可以为SPtr&lt;T&gt;定义一个新的构造函数用来从接受一个class U。<br>
template &lt;class T&gt;<br>
template &lt;class U&gt; SPtr&lt;T&gt;::SPtr (SPrt&lt;U&gt; & uptr)<br>
: _p (uptr.Release ())<br>
{}<br>
  这里的这个花招是模板在U不是T的子类的时候就不会编译成功(换句话说,只在U is-a T的时候才会编译)。这是因为uptr的缘故。Release()方法返回一个指向U的指针,并被赋值为_p,一个指向T的指针。所以如果U不是一个T的话,赋值会导致一个编译时刻错误。<br>
std::auto_ptr<br>
  后来我意识到在STL中的auto_ptr模板,就是我的Strong Pointer。在那时候还有许多的实现差异(auto_ptr的Release方法并不将内部的指针清零--你的编译器的库很可能用的就是这种陈旧的实现),但是最后在标准被广泛接受之前都被解决了。<br>
Transfer Semantics(转换语义学)<br>
  目前为止,我们一直在讨论在C++程序中资源管理的方法。宗旨是将资源封装到一些轻量级的类中,并由类负责它们的释放。特别的是,所有用new操作符分配的资源都会被储存并传递进Strong Pointer(标准库中的auto_ptr)的内部。<br>
  这里的关键词是传递(passing)。一个容器可以通过传值返回一个Strong Pointer来安全的释放资源。容器的客户只能够通过提供一个相应的Strong Pointer来保存这个资源。任何一个将结果赋给一个"裸"指针的做法都立即会被编译器发现。<br>
auto_ptr&lt;Item&gt; item = stack.Pop (); // ok<br>
Item * p = stack.Pop (); // Error! Type mismatch.<br>
  以传值方式被传递的对象有value semantics 或者称为 copy semantics。Strong Pointers是以值方式传递的--但是我们能说它们有copy semantics吗?不是这样的!它们所指向的对象肯定没有被拷贝过。事实上,传递过后,源auto_ptr不在访问原有的对象,并且目标 auto_ptr成为了对象的唯一拥有者(但是往往auto_ptr的旧的实现即使在释放后仍然保持着对对象的所有权)。自然而然的我们可以将这种新的行为称作Transfer Semantics。<br>
拷贝构造函数(copy construcor)和赋值操作符定义了auto_ptr的Transfer Semantics,它们用了非const的auto_ptr引用作为它们的参数。<br>
auto_ptr (auto_ptr&lt;T&gt; & ptr);<br>
auto_ptr & operator = (auto_ptr&lt;T&gt; & ptr);<br>
  这是因为它们确实改变了他们的源--剥夺了对资源的所有权。<br>
  通过定义相应的拷贝构造函数和重载赋值操作符,你可以将Transfer Semantics加入到许多对象中。例如,许多Windows中的资源,比如动态建立的菜单或者位图,可以用有Transfer Semantics的类来封装。<br>
Strong Vectors<br>
  标准库只在auto_ptr中支持资源管理。甚至连最简单的容器也不支持ownership semantics。你可能想将auto_ptr和标准容器组合到一起可能会管用,但是并不是这样的。例如,你可能会这样做,但是会发现你不能够用标准的方法来进行索引。<br>
vector&lt; auto_ptr&lt;Item&gt; &gt; autoVector;<br>
这种建造不会编译成功;<br>
Item * item = autoVector [0];<br>
另一方面,这会导致一个从autoVect到auto_ptr的所有权转换:<br>
auto_ptr&lt;Item&gt; item = autoVector [0];<br>
我们没有选择,只能够构造我们自己的Strong Vector。最小的接口应该如下:<br>
template &lt;class T&gt;<br>
class auto_vector<br>
{<br>
public:<br>
explicit auto_vector (size_t capacity = 0);<br>
T const * operator [] (size_t i) const;<br>
T * operator [] (size_t i);<br>
void assign (size_t i, auto_ptr&lt;T&gt; & p);<br>
void assign_direct (size_t i, T * p);<br>
void push_back (auto_ptr&lt;T&gt; & p);<br>
auto_ptr&lt;T&gt; pop_back ();<br>
};<br>
  你也许会发现一个非常防御性的设计态度。我决定不提供一个对vector的左值索引的访问,取而代之,如果你想设定(set)一个值的话,你必须用 assign或者assign_direct方法。我的观点是,资源管理不应该被忽视,同时,也不应该在所有的地方滥用。在我的经验里,一个strong vector经常被许多push_back方法充斥着。<br>
Strong vector最好用一个动态的Strong Pointers的数组来实现:<br>
template &lt;class T&gt;<br>
class auto_vector<br>
{<br>
private<br>
void grow (size_t reqCapacity);<br>
<br>
auto_ptr&lt;T&gt; *_arr;<br>
size_t _capacity;<br>
size_t _end;<br>
};<br>
  grow方法申请了一个很大的auto_ptr&lt;T&gt;的数组,将所有的东西从老的书组类转移出来,在其中交换,并且删除原来的数组。<br>
  auto_vector的其他实现都是十分直接的,因为所有资源管理的复杂度都在auto_ptr中。例如,assign方法简单的利用了重载的赋值操作符来删除原有的对象并转移资源到新的对象:<br>
void assign (size_t i, auto_ptr&lt;T&gt; & p)<br>
{<br>
_arr [i] = p;<br>
}<br>
我已经讨论了push_back和pop_back方法。push_back方法传值返回一个auto_ptr,因为它将所有权从auto_vector转换到auto_ptr中。<br>
对auto_vector的索引访问是借助auto_ptr的get方法来实现的,get简单的返回一个内部指针。<br>
T * operator [] (size_t i)<br>
{<br>
return _arr [i].get ();<br>
}<br>
  没有容器可以没有iterator。我们需要一个iterator让auto_vector看起来更像一个普通的指针向量。特别是,当我们废弃 iterator的时候,我们需要的是一个指针而不是auto_ptr。我们不希望一个auto_vector的iterator在无意中进行资源转换。<br>
template&lt;class T&gt;<br>
class auto_iterator: public<br>
iterator&lt;random_access_iterator_tag, T *&gt;<br>
{<br>
public:<br>
auto_iterator () : _pp (0) {}<br>
auto_iterator (auto_ptr&lt;T&gt; * pp) : _pp (pp) {}<br>
bool operator != (auto_iterator&lt;T&gt; const & it) const<br>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -