2

我目前正在设计一个大致如下所示的类层次结构:

struct Protocol
{
    // Pass lower-layer protocol as a reference.
    Protocol(Protocol & inLLProtocol) :
        mLLProtocol(inLLProtocol)
    {
    }

    // A protocol "always" has a LLProtocol.
    Protocol & mLLProtocol; 
};


struct Layer1Protocol : Protocol
{
    // This is the "bottom" protocol, so I pass a fake reference.
    Layer1Protocol() : Protocol(*static_cast<Protocol*>(nullptr)) {}
};

*nullptr只要从不访问引用,IIRC 绑定引用是安全的。所以现在我有责任设计我的 Layer1Protocol 类以防止这种情况发生。

我喜欢这种方法,因为我确保所有用户协议实例都会引用它们各自的低层协议(Layer1Protocol 是例外,但它是核心库的一部分)。我认为这比使用指针更可取,因为一旦引入指针,就可以传递空指针,然后可能需要在运行时检查,结果是大量的指针检查代码和偶尔的错误。

你认为我的基于参考的方法是可以辩护的吗?还是使用空引用总是不好的做法?

4

4 回答 4

11

只要从不访问引用,IIRC 绑定对 *nullptr 的引用是安全的。

不,取消引用nullptr始终是未定义的行为。在正确的程序中不能有“空引用”。

于 2012-05-25T09:55:06.900 回答
10

写作*static_cast<Protocol*>(nullptr)是取消引用nullptr,这会调用Undefined Behavior。你绝不能这样做。结果可以是墨菲想要的任何东西。这可能是您所说的“空引用”,但也可能是您(男性,AFAIK)怀孕,而程序就像您取消引用了一个有效指针一样工作。

所以,严格来说,没有“空引用”。只有未定义的行为

您的协议类总是并且不可避免地需要一个较低级别的协议,在这种情况下,您不能将它传递给它nullptr(甚至不能在引用中伪装)。或者它并不总是需要较低级别的协议,并且必须在访问它之前检查它是否有一个,在这种情况下,指针可以更好地实现您的设计约束。

于 2012-05-25T09:55:00.277 回答
2

您的方法与使用指针的等效方法有何不同,除了语法以及指针方法与引用方法不同,它不调用 UB 的事实之外?

如果您正在使用引用以避免检查空指针,但您随后传递(非法)空引用,那么您就否定了引用的优势。现在,您的引用可能突然为空,因此您可能需要像使用指针一样检查该条件。

如果,如您所说,您可以保证这些空引用中的任何一个都不会被取消引用——那么,如果您使用指针,同样的保证也将成立。

长话短说:指针不是不惜一切代价避免的。如果您遇到可能出现空值的情况,则需要使用指针——句点。您似乎担心必须到处检查空指针(可能是通过断言),但在大多数架构上,MMU 无论如何都会阻止您取消引用空指针——这实际上几乎与断言一样好。

于 2012-05-25T10:03:38.200 回答
1

您可能想查看 boost 中的“可选”类模板(我相信)。它允许您传递一个对象,但也允许您指定您故意不提供任何数据,这种方式比原始指针更安全。

这不是我自己特别喜欢的东西,但您可能会发现它符合您的要求。

于 2012-05-25T10:08:53.707 回答