您对在 C++ 中未找到抛出异常有何看法?

发布于 2024-07-05 13:11:00 字数 491 浏览 8 评论 0原文

我知道大多数人认为这是一种不好的做法,但是当你试图让你的类公共接口仅与引用一起使用,仅在必要时才将指针保留在内部时,我认为没有办法返回某些内容告诉您正在查找的值在容器中不存在。

class list {
    public:
        value &get(type key);
};

假设您不想在类的公共接口中看到危险的指针,在这种情况下如何返回 not find 并抛出异常?

你对此有何看法? 您是否返回一个空并检查它的空状态? 我实际上使用了 throw 方法,但引入了一种检查方法:

class list {
   public:
      bool exists(type key);
      value &get(type key);
};

因此,当我忘记首先检查该值是否存在时,我会得到一个异常,这实际上是一个异常

你会怎么做?

I know most people think that as a bad practice but when you are trying to make your class public interface only work with references, keeping pointers inside and only when necessary, I think there is no way to return something telling that the value you are looking doesn't exist in the container.

class list {
    public:
        value &get(type key);
};

Let's think that you don't want to have dangerous pointers being saw in the public interface of the class, how do you return a not found in this case, throwing an exception?

What is your approach to that? Do you return an empty value and check for the empty state of it? I actually use the throw approach but I introduce a checking method:

class list {
   public:
      bool exists(type key);
      value &get(type key);
};

So when I forget to check that the value exists first I get an exception, that is really an exception.

How would you do it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

很酷不放纵 2024-07-12 13:11:01

STL 通过使用迭代器来处理这种情况。 例如,std::map 类具有类似的功能:

iterator find( const key_type& key );

如果未找到键,则返回“end()”。 您可能想要使用此迭代器方法,或者对返回值使用某种包装器。

The STL deals with this situation by using iterators. For example, the std::map class has a similar function:

iterator find( const key_type& key );

If the key isn't found, it returns 'end()'. You may want to use this iterator approach, or to use some sort of wrapper for your return value.

莫相离 2024-07-12 13:11:01

正确的答案(根据 Alexandrescu 的说法)是:

Optional 和 Enforce

首先,一定要使用访问器,但要以更安全的方式,而不需要发明轮子:

boost::optional<X> get_X_if_possible();

然后创建一个 enforce helper:

template <class T, class E>
T& enforce(boost::optional<T>& opt, E e = std::runtime_error("enforce failed"))
{
    if(!opt)
    {
        throw e;
    }

    return *opt;
}

// and an overload for T const &

这样,根据值的缺失可能意味着什么,您可以显式检查:

if(boost::optional<X> maybe_x = get_X_if_possible())
{
    X& x = *maybe_x;

    // use x
}
else
{
    oops("Hey, we got no x again!");
}

或隐式检查:

X& x = enforce(get_X_if_possible());

// use x

当您担心效率,或者当您想要在发生故障时处理故障时,可以使用第一种方法。 第二种方法适用于所有其他情况。

The correct answer (according to Alexandrescu) is:

Optional and Enforce

First of all, do use the Accessor, but in a safer way without inventing the wheel:

boost::optional<X> get_X_if_possible();

Then create an enforce helper:

template <class T, class E>
T& enforce(boost::optional<T>& opt, E e = std::runtime_error("enforce failed"))
{
    if(!opt)
    {
        throw e;
    }

    return *opt;
}

// and an overload for T const &

This way, depending on what might the absence of the value mean, you either check explicitly:

if(boost::optional<X> maybe_x = get_X_if_possible())
{
    X& x = *maybe_x;

    // use x
}
else
{
    oops("Hey, we got no x again!");
}

or implicitly:

X& x = enforce(get_X_if_possible());

// use x

You use the first way when you’re concerned about efficiency, or when you want to handle the failure right where it occurs. The second way is for all other cases.

中二柚 2024-07-12 13:11:01

在这种情况下不要使用异常。 对于此类异常,C++ 具有不小的性能开销,即使没有抛出异常,而且它还使代码的推理变得更加困难(参见异常安全)。

C++ 的最佳实践是以下两种方法之一。 两者都在 STL 中使用:

  • 正如 Martin 指出的,返回一个迭代器。 实际上,你的迭代器很可能是一个简单指针的 typedef ,没有什么反对它的; 事实上,由于这与 STL 一致,您甚至可以认为这种方式优于返回引用。
  • 返回一个 std::pair。 但是,这使得修改该值变得不可能,因为调用了对的复制对象,而该复制对象不适用于引用成员。

/编辑:

这个答案引起了相当多的争议,从评论中可以看出,但从它得到的许多反对票中不那么明显。 我发现这相当令人惊讶。

这个答案从来都不是最终的参考点。 马丁已经给出了“正确”的答案:例外情况对本例中行为的反映相当差。 从语义上讲,使用其他一些信号机制比异常更有意义。

美好的。 我完全赞同这个观点。 无需再提一次。 相反,我想为答案提供一个额外的方面。 虽然微小的速度提升永远不应该成为任何决策的首要理由,但它们可以提供进一步的论据,并且在某些(少数)情况下,它们甚至可能至关重要。

实际上,我提到了两个方面:性能和异常安全。 我认为后者是没有争议的。 虽然提供强有力的异常保证非常困难(当然,最强的是“不抛出”),但我相信这是必要的:任何保证不抛出异常的代码都会使整个程序更容易推理。 许多 C++ 专家都强调这一点(例如 Scott Meyers 在《Effective C++》第 29 条中)。

关于速度。 Martin York 指出这在现代编译器中不再适用。 我谨表示不同意。 C++ 语言使得环境有必要在运行时跟踪在异常情况下可能展开的代码路径。 现在,这个开销实际上并没有那么大(并且很容易验证这一点)。 我上面的文字中的“不平凡”可能太强烈了。

然而,我发现区分 C++ 等语言和 C# 等许多现代“托管”语言很重要。 只要不抛出异常,后者就没有额外的开销,因为无论如何都会保留展开堆栈所需的信息。 总的来说,支持我的用词选择。

Don't use an exception in such a case. C++ has a nontrivial performance overhead for such exceptions, even if no exception is thrown, and it additially makes reasoning about the code much harder (cf. exception safety).

Best-practice in C++ is one of the two following ways. Both get used in the STL:

  • As Martin pointed out, return an iterator. Actually, your iterator can well be a typedef for a simple pointer, there's nothing speaking against it; in fact, since this is consistent with the STL, you could even argue that this way is superior to returning a reference.
  • Return a std::pair<bool, yourvalue>. This makes it impossible to modify the value, though, since a copycon of the pair is called which doesn't work with referende members.

/EDIT:

This answer has spawned quite some controversy, visible from the comments and not so visible from the many downvotes it got. I've found this rather surprising.

This answer was never meant as the ultimate point of reference. The “correct” answer had already been given by Martin: execeptions reflect the behaviour in this case rather poorly. It's semantically more meaningful to use some other signalling mechanism than exceptions.

Fine. I completely endorse this view. No need to mention it once again. Instead, I wanted to give an additional facet to the answers. While minor speed boosts should never be the first rationale for any decision-making, they can provide further arguments and in some (few) cases, they may even be crucial.

Actually, I've mentioned two facets: performance and exception safety. I believe the latter to be rather uncontroversial. While it's extremely hard to give strong exceptions guarantees (the strongest, of course, being “nothrow”), I believe it's essential: any code that is guaranteed to not throw exceptions makes the whole program easier to reason about. Many C++ experts emphasize this (e.g. Scott Meyers in item 29 of “Effective C++”).

About speed. Martin York has pointed out that this no longer applies in modern compilers. I respectfully disagree. The C++ language makes it necessary for the environment to keep track, at runtime, of code paths that may be unwound in the case of an exception. Now, this overhead isn't really all that big (and it's quite easy to verify this). “nontrivial” in my above text may have been too strong.

However, I find it important to draw the distinction between languages like C++ and many modern, “managed” languages like C#. The latter has no additional overhead as long as no exception is thrown because the information necessary to unwind the stack is kept anyway. By and large, stand by my choice of words.

南薇 2024-07-12 13:11:01

STL 迭代器?

我之前提出的“迭代器”想法很有趣,但迭代器的真正意义在于通过容器进行导航。 不是一个简单的访问器。

如果您的访问器是众多访问器之一,那么迭代器就是最佳选择,因为您将能够使用它们在容器中移动。 但是如果你的访问器是一个简单的 getter,能够返回或者没有值,那么你的迭代器可能只是一个美化的指针

......引导我们...

智能指针?

智能指针的目的是简化指针所有权。 使用共享指针,您将获得一个将被共享的资源(内存),但要付出一定的开销(共享指针需要分配一个整数作为引用计数器......)。

你必须选择:要么你的值已经在共享指针内,然后,你可以返回这个共享指针(或弱指针)。 或者您的值位于原始指针内。 然后您可以返回行指针。 如果您的资源尚未位于共享指针内,您不想返回共享指针:当您的共享指针超出范围并在不通知的情况下删除您的值时,将会发生一系列有趣的事情你...

:-p

指针?

如果您的接口清楚其资源的所有权,并且事实上返回值可以为 NULL,那么您可以返回一个简单的原始指针。 如果你的代码的用户足够愚蠢,忽略了你的对象的接口契约,或者用你的指针进行算术或其他任何事情,那么他/她将足够愚蠢地破坏你选择返回值的任何其他方式,所以不要理会智障人士...

未定义的值

除非您的值类型确实已经具有某种“未定义”值,并且用户知道这一点,并且会接受处理该值,否则这是一个可能的解决方案,类似于指针或迭代器解决方案。

但是,不要将“未定义”值添加到您的值类因为您提出的问题:您最终会将“引用与指针”战争提升到另一个疯狂的程度。 代码用户希望您提供给他们的对象要么正常,要么不存在。 必须测试该对象的所有其他代码行是否仍然有效是一件痛苦的事情,并且由于您的错误,将毫无用处地使用户代码变得复杂。

例外

例外通常并不像某些人希望的那样代价高昂。 但对于简单的访问器来说,如果您的访问器经常使用,则成本可能不小。

例如,STL std::vector 有两个通过索引访问其值的访问器:

T & std::vector::operator[]( /* index */ )

和:

T & std::vector::at( /* index */ )

区别在于 []非抛出< /i> . 因此,如果您访问向量的范围之外,您就得靠自己了,可能会面临内存损坏和迟早崩溃的风险。 因此,您应该真正确保您使用它验证了代码。

另一方面,at抛出。 这意味着如果您访问向量的范围之外,那么您将得到一个干净的异常。 如果您想将错误的处理委托给另一个代码,则此方法更好。

当我访问循环内的值或类似的东西时,我个人使用 [] 。 当我觉得异常是返回当前代码(或调用代码)(出现问题的事实)的好方法时,我使用 at

所以呢?

在您的情况下,您必须选择:

如果您确实需要闪电般快速的访问,那么投掷访问器可能会成为问题。 但这意味着您已经在代码上使用了分析器来确定这是瓶颈,不是吗?

;-)

如果您知道没有值的情况经常发生,并且/或者您希望客户端将可能的 null/无效/任何语义指针传播到所访问的值,则返回一个指针(如果您的值位于一个简单的值中)指针)或弱/共享指针(如果您的值由共享指针拥有)。

但是,如果您认为客户端不会传播此“空”值,或者他们不应该在其代码中传播 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() ;
   }
}

因此,如果您的主要问题是在上面的两个用户代码之间进行选择,那么您的问题不是性能问题,而是“代码人体工程学”。 因此,不应因为潜在的性能问题而放弃异常解决方案。

:-)

STL Iterators?

The "iterator" idea proposed before me is interesting, but the real point of iterators is navigation through a container. Not as an simple accessor.

If you're accessor is one among many, then iterators are the way to go, because you will be able to use them to move in the container. But if your accessor is a simple getter, able to return either the value or the fact there is no value, then your iterator is perhaps only a glorified pointer...

Which leads us to...

Smart pointers?

The point of smart pointers is to simplify pointer ownership. With a shared pointer, you'll get a ressource (memory) which will be shared, at the cost of an overhead (shared pointers needs to allocate an integer as a reference counter...).

You have to choose: Either your Value is already inside a shared pointer, and then, you can return this shared pointer (or a weak pointer). Or Your value is inside a raw pointer. Then you can return the row pointer. You don't want to return a shared pointer if your ressource is not already inside a shared pointer: A World of funny things will happen when your shared pointer will get out of scope an delete your Value without telling you...

:-p

Pointers?

If your interface is clear about its ownership of its ressources, and by the fact the returned value can be NULL, then you could return a simple, raw pointer. If the user of your code is dumb enough ignore the interface contract of your object, or to play arithmetics or whatever with your pointer, then he/she will be dumb enough to break any other way you'll choose to return the value, so don't bother with the mentally challenged...

Undefined Value

Unless your Value type really has already some kind of "undefined" value, and the user knows that, and will accept to handle that, it is a possible solution, similar to the pointer or iterator solution.

But do not add a "undefined" value to your Value class because of the problem you asked: You'll end up raising the "references vs. pointer" war to another level of insanity. Code users want the objects you give them to either be Ok, or to not exist. Having to test every other line of code this object is still valid is a pain, and will complexify uselessly the user code, by your fault.

Exceptions

Exceptions are usually not as costly as some people would like them to be. But for a simple accessor, the cost could be not trivial, if your accessor is used often.

For example, the STL std::vector has two accessors to its value through an index:

T & std::vector::operator[]( /* index */ )

and:

T & std::vector::at( /* index */ )

The difference being that the [] is non-throwing . So, if you access outside the range of the vector, you're on your own, probably risking memory corruption, and a crash sooner or later. So, you should really be sure you verified the code using it.

On the other hand, at is throwing. This means that if you access outside the range of the vector, then you'll get a clean exception. This method is better if you want to delegate to another code the processing of an error.

I use personnaly the [] when I'm accessing the values inside a loop, or something similar. I use at when I feel an exception is the good way to return the current code (or the calling code) the fact something went wrong.

So what?

In your case, you must choose:

If you really need a lightning-fast access, then the throwing accessor could be a problem. But this means you already used a profiler on your code to determinate this is a bottleneck, doesn't it?

;-)

If you know that not having a value can happen often, and/or you want your client to propagate a possible null/invalid/whatever semantic pointer to the value accessed, then return a pointer (if your value is inside a simple pointer) or a weak/shared pointer (if your value is owned by a shared pointer).

But if you believe the client won't propagate this "null" value, or that they should not propagate a NULL pointer (or smart pointer) in their code, then use the reference protected by the exception. Add a "hasValue" method returning a boolean, and add a throw should the user try the get the value even if there is none.

Last but not least, consider the code that will be used by the user of your object:

// 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() ;
   }
}

So, if your main problem is choosing between the two user codes above, your problem is not about performance, but "code ergonomy". Thus, the exception solution should not be put aside because of potential performance issues.

:-)

冷默言语 2024-07-12 13:11:01

contains() 的问题在于,您最终将搜索两次确实存在的东西(首先检查它是否在那里,然后再次找到它)。 这是低效的,特别是如果(正如其“列表”名称所示)您的容器的搜索时间为 O(n)。

当然,您可以进行一些内部缓存来避免双重搜索,但是您的实现会变得更加混乱,您的类变得不那么通用(因为您已经针对特定情况进行了优化),并且它可能不会是异常安全或线程的-安全的。

The problem with exists() is that you'll end up searching twice for things that do exist (first check if it's in there, then find it again). This is inefficient, particularly if (as its name of "list" suggests) your container is one where searching is O(n).

Sure, you could do some internal caching to avoid the double search, but then your implementation gets messier, your class becomes less general (since you've optimised for a particular case), and it probably won't be exception-safe or thread-safe.

清晰传感 2024-07-12 13:11:01

返回一个shared_ptr作为结果怎么样? 如果未找到该项目,则该值可能为空。 它的工作方式类似于指针,但它会为您释放对象。

How about returning a shared_ptr as the result. This can be null if the item wasn't found. It works like a pointer, but it will take care of releasing the object for you.

〆凄凉。 2024-07-12 13:11:01

(我意识到这并不总是正确的答案,而且我的语气有点强硬,但您应该在决定其他更复杂的替代方案之前考虑这个问题):

那么,什么是返回指针错误?

我在 SQL 中多次看到这种情况,人们会认真地从不处理 NULL 列,就像他们患有某种传染性疾病或其他什么一样。 相反,他们巧妙地提出了一个“空白”或“不存在”的人为值,例如 -1、9999 甚至“@X-EMPTY-X@”之类的值。

我的回答是:语言已经有了“不在那里”的结构; 来吧,不要害怕使用它。

(I realize this is not always the right answer, and my tone a bit strong, but you should consider this question before deciding for other more complex alternatives):

So, what's wrong with returning a pointer?

I've seen this one many times in SQL, where people will do their earnest to never deal with NULL columns, like they have some contagious decease or something. Instead, they cleverly come up with a "blank" or "not-there" artificial value like -1, 9999 or even something like '@X-EMPTY-X@'.

My answer: the language already has a construct for "not there"; go ahead, don't be afraid to use it.

狼亦尘 2024-07-12 13:11:01

附件?

我之前提出的“迭代器”想法很有趣,但迭代器的真正意义在于通过容器进行导航。 不是作为一个简单的访问器。

我同意paercebal,迭代器就是迭代。 我不喜欢STL 的方式。 但访问器的想法似乎更有吸引力。 那么我们需要什么呢? 类似容器的类,感觉像是用于测试的布尔值,但行为类似于原始返回类型。 这对于演员操作员来说是可行的。

template <T> class Accessor {
    public:
        Accessor(): _value(NULL) 
        {}

        Accessor(T &value): _value(&value)
        {}

        operator T &() const
        {
            if (!_value)
               throw Exception("that is a problem and you made a mistake somewhere.");
            else
               return *_value;
        }

        operator bool () const
        {
            return _value != NULL;
        }

    private:
        T *_value;
};

现在,有什么可预见的问题吗? 用法示例:

Accessor <type> value = list.get(key);

if (value) {
   type &v = value;

   v.doSomething();
}

Accessor?

The "iterator" idea proposed before me is interesting, but the real point of iterators is navigation through a container. Not as an simple accessor.

I agree with paercebal, an iterator is to iterate. I don't like the way STL does. But the idea of an accessor seems more appealing. So what we need? A container like class that feels like a boolean for testing but behaves like the original return type. That would be feasible with cast operators.

template <T> class Accessor {
    public:
        Accessor(): _value(NULL) 
        {}

        Accessor(T &value): _value(&value)
        {}

        operator T &() const
        {
            if (!_value)
               throw Exception("that is a problem and you made a mistake somewhere.");
            else
               return *_value;
        }

        operator bool () const
        {
            return _value != NULL;
        }

    private:
        T *_value;
};

Now, any foreseeable problem? An example usage:

Accessor <type> value = list.get(key);

if (value) {
   type &v = value;

   v.doSomething();
}
一口甜 2024-07-12 13:11:01

@aradtke,你说。

我同意paercebal,迭代器是
迭代。 我不喜欢STL的方式
做。 但是访问器的想法
似乎更有吸引力。 那么我们需要什么呢?
一个类似容器的类,感觉就像
用于测试的布尔值,但行为类似于
原始返回类型。 那会
对于强制转换运算符来说是可行的。 [..] 现在,
有什么可预见的问题吗?

首先,您不需要 OPERATOR bool。 有关详细信息,请参阅Safe Bool 习语。 但是关于你的问题......

这就是问题,用户现在需要在案例中显式转换。 类似指针的代理(例如迭代器、ref-counted-ptrs 和原始指针)有简洁的“get”语法。 如果调用者必须使用额外的代码来调用转换运算符,则提供转换运算符并不是很有用。

从您的参考示例开始,最简洁的编写方式:

// 'reference' style, check before use
if (Accessor<type> value = list.get(key)) {
   type &v = value;
   v.doSomething();
}
// or
if (Accessor<type> value = list.get(key)) {
   static_cast<type&>(value).doSomething();
}

这没关系,不要误会我的意思,但它比它必须的更冗长。 现在考虑一下,如果我们知道,出于某种原因,list.get 将会成功。 然后:

// 'reference' style, skip check 
type &v = list.get(key);
v.doSomething();
// or
static_cast<type&>(list.get(key)).doSomething();

现在让我们回到迭代器/指针行为:

// 'pointer' style, check before use
if (Accessor<type> value = list.get(key)) {
   value->doSomething();
}

// 'pointer' style, skip check 
list.get(key)->doSomething();

两者都非常好,但指针/迭代器语法稍微短一点。 您可以为“引用”样式提供一个成员函数“get()”...但这已经是operator*() 和operator->() 的用途。

“指针”样式访问器现在具有运算符“未指定的布尔”、运算符*和运算符->。

你猜怎么着……原始指针满足这些要求,因此对于原型设计,list.get() 返回 T* 而不是 Accessor。 然后等list的设计稳定了,就可以回来写Accessor了,一个类似指针的Proxy< /a> 类型。

@aradtke, you said.

I agree with paercebal, an iterator is
to iterate. I don't like the way STL
does. But the idea of an accessor
seems more appealing. So what we need?
A container like class that feels like
a boolean for testing but behaves like
the original return type. That would
be feasible with cast operators. [..] Now,
any foreseeable problem?

First, YOU DO NOT WANT OPERATOR bool. See Safe Bool idiom for more info. But about your question...

Here's the problem, users need to now explict cast in cases. Pointer-like-proxies (such as iterators, ref-counted-ptrs, and raw pointers) have a concise 'get' syntax. Providing a conversion operator is not very useful if callers have to invoke it with extra code.

Starting with your refence like example, the most concise way to write it:

// 'reference' style, check before use
if (Accessor<type> value = list.get(key)) {
   type &v = value;
   v.doSomething();
}
// or
if (Accessor<type> value = list.get(key)) {
   static_cast<type&>(value).doSomething();
}

This is okay, don't get me wrong, but it's more verbose than it has to be. now consider if we know, for some reason, that list.get will succeed. Then:

// 'reference' style, skip check 
type &v = list.get(key);
v.doSomething();
// or
static_cast<type&>(list.get(key)).doSomething();

Now lets go back to iterator/pointer behavior:

// 'pointer' style, check before use
if (Accessor<type> value = list.get(key)) {
   value->doSomething();
}

// 'pointer' style, skip check 
list.get(key)->doSomething();

Both are pretty good, but pointer/iterator syntax is just a bit shorter. You could give 'reference' style a member function 'get()'... but that's already what operator*() and operator->() are for.

The 'pointer' style Accessor now has operator 'unspecified bool', operator*, and operator->.

And guess what... raw pointer meets these requirements, so for prototyping, list.get() returns T* instead of Accessor. Then when the design of list is stable, you can come back and write the Accessor, a pointer-like Proxy type.

素手挽清风 2024-07-12 13:11:01

在这种情况下,我更喜欢做的是抛出“get”,对于那些性能问题或失败很常见的情况,有一个“tryGet”函数,类似于“bool tryGet(type key, value **pp)”约定是,如果返回 true,则 *pp == 指向某个对象的有效指针,否则 *pp 为 null。

what I prefer doing in situations like this is having a throwing "get" and for those circumstances where performance matter or failiure is common have a "tryGet" function along the lines of "bool tryGet(type key, value **pp)" whoose contract is that if true is returned then *pp == a valid pointer to some object else *pp is null.

執念 2024-07-12 13:11:01

有趣的问题。 我猜想在 C++ 中专门使用引用是一个问题 - 在 Java 中引用更灵活并且可以为空。 我不记得强制空引用是否是合法的 C++:

MyType *pObj = nullptr;
return *pObj

但我认为这很危险。 同样,在 Java 中,我会抛出异常,因为这在 Java 中很常见,但我很少看到在 C++ 中如此自由地使用异常。
如果我正在为可重用的 C++ 组件制作公共 API 并且必须返回引用,我想我会走异常路线。
我真正的偏好是让 API 返回一个指针; 我认为指针是 C++ 的一个组成部分。

Interesting question. It's a problem in C++ to exclusively use references I guess - in Java the references are more flexible and can be null. I can't remember if it's legal C++ to force a null reference:

MyType *pObj = nullptr;
return *pObj

But I consider this dangerous. Again in Java I'd throw an exception as this is common there, but I rarely see exceptions used so freely in C++.
If I was making a puclic API for a reusable C++ component and had to return a reference, I guess I'd go the exception route.
My real preference is to have the API return a pointer; I consider pointers an integral part of C++.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文