在许多情况下,Foo*& 可以替代Foo**。在这两种情况下,您都有一个可以修改其地址的指针。
假设你有一个抽象的非值类型,需要返回,但是返回值被错误码占用了:
error_code get_foo( Foo** ppfoo )
或
error_code get_foo( Foo*& pfoo_out )
现在,可变的函数参数很少有用,因此更改最外层指针ppFoo指向的位置的能力很少有用。然而,一个指针是可为空的——所以如果get_foo的参数是可选的,一个指针就像一个可选的引用。
在这种情况下,返回值是一个原始指针。如果它返回一个拥有的资源,它通常应该是一个std::unique_ptr<Foo>*——一个处于该间接级别的智能指针。
如果相反,它返回一个指向它不共享所有权的东西的指针,那么原始指针更有意义。
除了这些“粗略的参数”之外,Foo** 还有其他用途。如果你有一个多态的非值类型,非拥有句柄是Foo*,同样的原因你想要一个int*你想要一个Foo**。
然后你会问“你为什么想要int*?”在现代 C++ 中,int* 是对 int 的非拥有可为空的可变引用。当存储在 struct 中时,它的行为比引用更好(结构中的引用会在分配和复制周围产生令人困惑的语义,尤其是与非引用混合时)。
您有时可以将 int* 替换为 std::reference_wrapper<int>,以及 std::optional<std::reference_wrapper<int>>,但请注意,它将是简单 int* 的 2 倍。
所以有正当理由使用int*。一旦你有了它,当你想要一个指向非值类型的指针时,你可以合法地使用Foo**。你甚至可以通过一个连续的int*s 数组来访问int**。
合法地成为三星级程序员变得越来越难。现在你需要一个正当的理由(比如说)想通过间接传递Foo**。通常早在你达到那个点之前,你就应该考虑抽象和/或简化你的代码结构。
所有这些都忽略了最常见的原因;与 C API 交互。 C 没有unique_ptr,也没有span。它倾向于使用原始类型而不是结构,因为结构需要笨拙的基于函数的访问(没有运算符重载)。
因此,当 C++ 与 C 交互时,您有时会比等效的 C++ 代码多获得 0-3 个*s。