【发布时间】:2011-02-02 18:28:51
【问题描述】:
我可以想象一种情况,其中输入参数可以为 NULL,因此首选传递指针而不是传递引用?
任何人都可以添加更多案例吗?
【问题讨论】:
-
似乎无法想到任何 有用 的情况,即通过指针传递优于通过引用传递,除了您指出传递 NULL 实际上意味着什么的情况.
标签: c++
我可以想象一种情况,其中输入参数可以为 NULL,因此首选传递指针而不是传递引用?
任何人都可以添加更多案例吗?
【问题讨论】:
标签: c++
在处理原始内存时(例如,如果创建您自己的内存池),您会希望使用指针。但你是对的,在普通代码中,指针的唯一用途是可选参数。
【讨论】:
在传递的对象实际上要被修改的情况下,有些人更喜欢按指针传递。当对象通过引用传递时,它们使用 pass-by-const-reference 以避免对象的副本,但不会在函数中更改。
在说明中,取以下函数:
int foo(int x);
int foo1(int &x);
int foo2(int *x);
现在在代码中,我执行以下操作:
int testInt = 0;
foo(testInt); // can't modify testInt
foo1(testInt); // can modify testInt
foo2(&testInt); // can modify testInt
在调用 foo 与 foo1 时,从调用者的角度(或阅读代码的程序员)来看,函数可以修改 testInt 而无需查看函数的签名并不明显。看 foo2 ,读者可以很容易地看到该函数实际上可能会修改 testInt 的值,因为该函数正在接收参数的地址。请注意,这并不能保证对象被实际修改,但这就是在使用引用和指针时保持一致的地方。一般来说,如果您想始终遵循此准则,则应始终在希望避免复制时传递 const 引用,并在希望能够修改对象时通过指针传递。
【讨论】:
foo1(testInt) 修改 foo1,如果在函数调用之后 testInt 不同,我会感到有点惊讶(尽管我最终会通过检查函数定义来弄清楚)。但是对于foo2(&testInt),很明显 testInt 可用于函数修改。优秀的答案。我认为这是一个特别重要的标准,在诸如教科书之类的自动完成(将显示函数定义)等工具不可用的情况下采用。
NULL 的情况,无论const 与否(你应该在讨论中提到的另一件事)期望被调用者修改或不修改事物并采取额外步骤来强制执行,如果我们要走那条路线。) :) 最重要的是,您忘记提及 operator= 对指针与引用的影响!
在 C++ 中,大多数情况下几乎不需要通过指针传递。您应该首先考虑替代方案:模板、(const)引用、容器、字符串和智能指针。也就是说,如果您必须支持遗留代码,那么您将需要使用指针。如果您的编译器是简约的(例如嵌入式系统),您将需要指针。如果您需要与 C 库(您正在使用的非常特殊的驱动程序的某种系统库?)交谈,那么您将需要使用指针。如果你想处理非常特殊的内存偏移量,那么你需要指针。
在 C 中,指针是一等公民,它们太基础了,无法考虑消除它们。
【讨论】:
可以想象,可以编写一个函数来对内存做一些事情,例如重新分配它以使其更大(返回新指针)。
【讨论】:
任何时候你传递一个函数指针。 或者,如果你有一个“重置”方法,比如 auto_ptr。
【讨论】:
如果我需要传递一个对象数组,我需要通过指针传递。数组并不总是存储在std::vector<>。
除此之外,按指针传递允许 NULL 而按引用传递则不允许,因此我将这种区别用作合同,就像 SQL 中的 NULL 与 NOT NULL 列一样,大致类似于“函数返回 @987654324 @:true = 成功,false = 失败,而函数返回 int:返回值是一个结果代码,其中0 = 成功,其他都是失败。
【讨论】:
vector 中。也就是说,这是一个问题中的漏洞:如果您打算修改指针,您会使用双指针还是引用? (问题不在于参数列表中的 a 指针,而在于当您需要在函数中修改指针与引用时。在您的情况下,指针只是副产品。)
vector 中的数据,而且不应该这样。当您获得一个数据包时,您希望直接对该数据进行操作,而不是浪费时间调整向量大小并将数据复制到其中。此外,您可以使用指针轻松地进行转置操作或跨步(每个第 n 个元素)操作。 BLAS 类型的代码本质上永远不会使用vector;如果您尝试,您将有很多&vec[0] 代码。 (强迫一切都存在于vectors 是 C++ 被不公平地指控导致代码膨胀的原因之一。如果你不需要它,请不要使用它。)
vector<char> buffer(bufferSize); recv(&buffer[0], buffer.size()); 等等?如果要将数据复制到向量中,为什么不直接将其填充到向量中呢?当然,在使用旧样式代码时,“使用矢量”的建议就没有了。在新代码中,我从未遇到过处理原始内存的理由。 (说真的,向量只是这样做,但确保它被删除就是全部。对于原始内存分配,没有什么是你不能用向量做的。)
在现实世界的编程中有很多情况,其中参数不存在或无效,这可能取决于代码的运行时语义。在这种情况下,您可以使用 NULL (0) 来表示此状态。除此之外,
虽然如果花足够的时间正确设计代码,这些情况是可以避免的;实际上,不可能每次都这样做。
【讨论】:
除了关于所有权语义(尤其是工厂函数)的一些其他答案。
虽然不是技术原因,但常见的样式指南要求是任何可以修改的参数都应该通过指针传递。这使得在调用点很明显可以修改对象。
void Operate(const int &input_arg, int *output_arg) {
*output_arg = input_arg + 1;
}
int main() {
int input_arg = 5;
int output_arg;
Foo(input_arg, &output_arg); // Passing address, will be modified!
}
【讨论】:
当您需要操作(例如调整大小、重新分配等)函数内部指针所指向的地址时,您需要一个指针。
例如:
void f( int* p )
{
// ..
delete []p;
p = new int[ size ];
//...
}
与引用不同,指针的值可以更改和操作。
【讨论】:
auto_ptr 或更好的unique_ptr 来获得所有权。
这不是专门的参数传递,但它确实会影响参数传递。
您可以拥有指针的容器/聚合,但不能拥有引用。尽管引用是多态的,但只有指针支持对容器使用至关重要的“更新”操作(特别是因为您还不能批量初始化引用,不确定 C++0x 聚合初始化器是否会改变这一点)。
因此,如果您有一个充满指针的容器,您通常最终会使用接受指针而不是引用的函数来管理它。
【讨论】:
Boost.Pointer Containers 库。我只希望“索引”案例存在没有所有权的版本:/
& 运算符将对目标对象执行它。虽然您可以使用引用参数来查找目标的地址,但我仍然强烈地感觉到我之前评论中的指导“如果您要以任何方式存储参数的地址,请让调用者明确地传递该地址"是正确的。
C++ FAQ 对这个问题有很好的回答:
尽可能使用参考文献,并且 必要时指点。
参考文献通常优先于 不需要时的指针 “重新安置”。这通常意味着 参考是最有用的 类的公共接口。参考 通常出现在皮肤上 对象和内部的指针。
上述情况的例外是 函数的参数或返回值 需要一个“哨兵”参考——一个 不指代的参考 目的。这通常最好由 返回/获取指针,并给出 这个特殊的NULL指针 意义(参考应始终 别名对象,而不是取消引用的 NULL 指针)。
注意:老 C 程序员有时 不喜欢参考,因为他们 提供不是的参考语义 显式在调用者的代码中。后 一些 C++ 经验,但是,一个 很快意识到这是一种形式 信息隐藏,这是一种资产 而不是责任。例如。, 程序员应该在 问题的语言而不是 机器的语言。
【讨论】:
规则一:如果 NULL 是函数上下文中函数参数的有效值,则将其作为指针传递,否则作为引用传递。
基本原理,如果它不能(不应该!)永远是 NULL,那么不要让自己经历检查 NULL 的麻烦或因为它是 NULL 而冒着问题的风险。
【讨论】:
Google 似乎对此有强烈的看法,我倾向于同意:
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments
【讨论】: