STL 迭代器?
摆在我面前的“迭代器”想法很有趣,但迭代器的真正意义在于通过容器导航。不是一个简单的访问器。
如果您的访问器是众多访问器之一,那么迭代器就是您要走的路,因为您将能够使用它们在容器中移动。但是,如果您的访问器是一个简单的 getter,能够返回value或没有 value的事实,那么您的迭代器可能只是一个美化的指针......
这导致我们...
智能指针?
智能指针的重点是简化指针所有权。使用共享指针,您将获得一个共享的资源(内存),代价是开销(共享指针需要分配一个整数作为引用计数器......)。
您必须选择:您的值已经在共享指针中,然后,您可以返回此共享指针(或弱指针)。或者您的值在原始指针内。然后您可以返回行指针。如果您的资源尚未在共享指针中,则您不想返回共享指针:当您的共享指针超出范围并在不告诉您的情况下删除您的值时,将会发生很多有趣的事情......
:-p
指针?
如果您的接口清楚地知道其资源的所有权,并且返回的值可以是 NULL,那么您可以返回一个简单的原始指针。如果您的代码的用户足够愚蠢,可以忽略对象的接口契约,或者用您的指针进行算术运算或其他任何操作,那么他/她将足够愚蠢以破坏您选择返回值的任何其他方式,所以智障者不要打扰...
未定义值
除非您的 Value 类型确实已经具有某种“未定义”值,并且用户知道这一点并愿意接受处理,否则它是一种可能的解决方案,类似于指针或迭代器解决方案。
但是不要因为您提出的问题而在您的 Value 类中添加“未定义”值:您最终会将“引用与指针”之战提升到另一个疯狂程度。代码用户希望您提供给他们的对象要么正常,要么不存在。必须测试该对象仍然有效的所有其他代码行是一种痛苦,并且会因您的错而使用户代码变得无用地复杂化。
例外
例外通常不像某些人希望的那样昂贵。但是对于一个简单的访问器,如果您的访问器经常使用,成本可能不是微不足道的。
例如,STL std::vector 有两个通过索引访问其值的访问器:
T & std::vector::operator[]( /* index */ )
和:
T & std::vector::at( /* index */ )
不同之处[]
在于是非投掷。因此,如果您在向量范围之外进行访问,则您只能靠自己,可能会冒着内存损坏的风险,并且迟早会崩溃。所以,你真的应该确保你使用它验证了代码。
另一方面,at
是投掷。这意味着如果您在向量范围之外访问,那么您将得到一个干净的异常。如果您想将错误处理委托给另一个代码,则此方法会更好。
[]
当我访问循环内的值或类似的东西时,我会使用personnaly 。当我感觉到异常时,我使用at
是返回当前代码(或调用代码)出错事实的好方法。
所以呢?
在您的情况下,您必须选择:
如果您真的需要闪电般的快速访问,那么投掷访问器可能是个问题。但这意味着您已经在代码上使用了分析器来确定这是一个瓶颈,不是吗?
;-)
如果您知道没有值可能经常发生,并且/或者您希望您的客户端将可能的空/无效/任何语义指针传播到访问的值,然后返回一个指针(如果您的值在一个简单的指针内)或弱/共享指针(如果您的值由共享指针拥有)。
但是,如果您认为客户端不会传播此“空”值,或者他们不应该在其代码中传播 NULL 指针(或智能指针),那么请使用受异常保护的引用。添加一个返回布尔值的“hasValue”方法,并在用户尝试获取值时添加一个抛出,即使没有。
最后但同样重要的是,考虑对象用户将使用的代码:
// If you want your user to have this kind of code, then choose either
// pointer or smart pointer solution
void doSomething(MyClass & p_oMyClass)
{
MyValue * pValue = p_oMyClass.getValue() ;
if(pValue != NULL)
{
// Etc.
}
}
MyValue * doSomethingElseAndReturnValue(MyClass & p_oMyClass)
{
MyValue * pValue = p_oMyClass.getValue() ;
if(pValue != NULL)
{
// Etc.
}
return pValue ;
}
// ==========================================================
// If you want your user to have this kind of code, then choose the
// throwing reference solution
void doSomething(MyClass & p_oMyClass)
{
if(p_oMyClass.hasValue())
{
MyValue & oValue = p_oMyClass.getValue() ;
}
}
因此,如果您的主要问题是在上述两个用户代码之间进行选择,那么您的问题不在于性能,而在于“代码人机工程学”。因此,不应因为潜在的性能问题而搁置异常解决方案。
:-)