c++ - pointer中文 - pointer是什么




何时使用引用与指针 (12)

我理解指针与引用的语法和一般语义,但是我应该如何决定何时适合在API中使用引用或指针?

当然,有些情况需要一个或另一个( operator++需要一个引用参数),但通常我发现我更喜欢使用指针(和指针),因为语法清楚地表明变量正在被破坏性地传递。

例如在下面的代码中:

void add_one(int& n) { n += 1; }
void add_one(int* const n) { *n += 1; }
int main() {
  int a = 0;
  add_one(a); // Not clear that a may be modified
  add_one(&a); // 'a' is clearly being passed destructively
}

随着指针,它总是(更)明显是怎么回事,所以对于API和类似的问题,清晰度是一个大问题,指针不比引用更合适? 这是否意味着只能在必要时使用引用(例如operator++ )? 是否有任何性能问题与其中一个?

编辑(OUTDATED):

除了允许NULL值和处理原始数组外,似乎选择归结为个人偏好。 我已经接受了下面引用Google的C ++风格指南的答案,因为它们提出了“引用可能会令人困惑,因为它们具有值语法,但是指针语义。”。

由于需要额外的工作来清理不应该为NULL的指针参数(例如, add_one(0)将在运行时调用指针版本和中断),所以从可维护性的角度来看,使用引用必须存在对象丢失句法清晰度是一种耻辱。


尽可能使用参考 ”规则存在问题,如果您想继续参考以供进一步使用,则会出现此问题。 为了用示例来说明这一点,假设您有以下课程。

class SimCard
{
    public:
        explicit SimCard(int id):
            m_id(id)
        {
        }

        int getId() const
        {
            return m_id;
        }

    private:
        int m_id;
};

class RefPhone
{
    public:
        explicit RefPhone(const SimCard & card):
            m_card(card)
        {
        }

        int getSimId()
        {
            return m_card.getId();
        }

    private:
        const SimCard & m_card;
};

起初,通过引用传递RefPhone(const SimCard & card)构造RefPhone(const SimCard & card)参数似乎是个好主意,因为它可以防止将错误/空指针传递给构造函数。 它以某种方式鼓励在栈上分配变量并从RAII中获益。

PtrPhone nullPhone(0);  //this will not happen that easily
SimCard * cardPtr = new SimCard(666);  //evil pointer
delete cardPtr;  //muahaha
PtrPhone uninitPhone(cardPtr);  //this will not happen that easily

但那时临时来破坏你的快乐世界。

RefPhone tempPhone(SimCard(666));   //evil temporary
//function referring to destroyed object
tempPhone.getSimId();    //this can happen

所以如果你盲目地坚持引用,那么就可能会传递无效指针,以便存储对被销毁对象的引用,这些引用基本上具有相同的效果。

编辑:请注意,我坚持遵守规则:“尽可能使用引用,只要你必须指针,避免指针,直到你不能。” 从最高的答复和接受的答案(其他答案也建议如此)。 虽然它应该是显而易见的,但实例并不表明这样的引用是不好的。 然而,它们可能会被误用,就像指针一样,它们可能会对代码带来自己的威胁。

指针和引用之间存在以下差异。

  1. 当涉及到传递变量时,通过引用传递看起来像传递值,但具有指针语义(像指针一样行为)。
  2. 引用不能直接初始化为0(空)。
  3. 引用(引用,未引用的对象)不能被修改(相当于“* const”指针)。
  4. const引用可以接受临时参数。
  5. 本地const引用延长了临时对象的生命周期

考虑到我目前的规则如下。

  • 在参数中使用将在函数范围内本地使用的参数。
  • 当0(空)是可接受的参数值时使用指针,或者您需要存储参数以供进一步使用。 如果0(空)是可接受的,我将“_n”后缀添加到参数中,使用保护指针(如Qt中的QPointer)或者只是记录它。 您也可以使用智能指针。 你必须更加小心共享指针而不是普通指针(否则你可能会因设计内存泄漏和责任混乱而结束)。

Use references as a last resort. Allocate an instance on the stack or the heap, use them.

使用参数范围的引用来获得最小的影响。如果你使用引用,因为指针对你来说太难了,那么移动到另一种语言。


wiki复制 -

这样做的结果是,在许多实现中,通过引用对具有自动或静态生命周期的变量进行操作,尽管在语法上类似于直接访问它,但可能涉及昂贵的隐含解引用操作。 引用是C ++的一个语法上有争议的特性,因为它们掩盖了标识符的间接级别; 也就是说,与指针通常在语法上突出的C代码不同,在C ++代码的大块中,如果被访问的对象被定义为本地或全局变量,或者它是一个引用(隐式指针)其他位置,特别是如果代码混合引用和指针。 这方面可以使编写不好的C ++代码更难以读取和调试(请参阅别名)。

我同意100%,这就是为什么我相信你只有在有足够理由这样做时才应该使用参考。


C ++ FAQ Lite -

尽可能使用参考,必要时使用指示。

只要您不需要“重新设置”,引用通常优先于指针。 这通常意味着引用在类的公共接口中最有用。 引用通常出现在对象的皮肤上,指向内部的指针。

上述例外是函数的参数或返回值需要一个“标记”引用 - 一个不引用对象的引用。 这通常最好通过返回/获取指针来完成,并且赋予NULL指针这种特殊意义(引用必须始终是别名对象,而不是取消引用的空指针)。

注意:旧的C语言程序员有时不喜欢引用,因为它们提供的引用语义在调用者的代码中并不明确。 但是,经过一些C ++经验,人们很快意识到这是一种信息隐藏,这是一种资产而不是责任。 例如,程序员应该用问题的语言来编写代码,而不是使用机器的语言。


任何性能差异都会很小,以致于不适合使用不太清楚的方法。

首先,一个引用通常比较const地方没有提到的情况是const引用。 对于非简单类型,传递一个const reference可以避免创建一个临时的,并且不会引起你关心的混乱(因为该值没有被修改)。 在这里,强迫一个人传递一个指针会导致你担心的混乱,因为看到地址被传递给一个函数可能会让你认为这个值发生了变化。

无论如何,我基本同意你的看法。 我不喜欢函数在引用时修改它们的值,当它不是很明显时,这是函数的功能。 在这种情况下,我也更喜欢使用指针。

当你需要返回一个复杂类型的值时,我倾向于使用引用。 例如:

bool GetFooArray(array &foo); // my preference
bool GetFooArray(array *foo); // alternative

在这里,函数名称清楚地表明您将数据返回到数组中。 所以没有混淆。

引用的主要优点是它们总是包含有效值,比指针更清晰,并且支持多态而不需要任何额外的语法。 如果这些优点都不适用,则没有理由偏好对指针的引用。


像其他人一样已经回答:总是使用引用,除非变量是NULL / nullptr 实际上是一个有效的状态。

约翰卡马克关于这个问题的观点是相似的:

NULL指针是C / C ++中最大的问题,至少在我们的代码中。 双重使用单个值作为标志和地址会导致令人难以置信的致命问题。 只要可能,C ++引用应该优先于指针; 而引用是“真正”只是一个指针,它具有不为NULL的隐式约定。 指针变为引用时执行NULL检查,然后您可以忽略此问题。

http://www.altdevblogaday.com/2011/12/24/static-code-analysis/

编辑2012-03-13

用户Bret Kuhns正确地评论道:

C ++ 11标准已经完成。 我认为现在是时候在这个线程中提到大多数代码应该完全正确地使用引用,shared_ptr和unique_ptr的组合。

真的够了,但问题仍然存在,即使用智能指针替换原始指针。

例如, std::unique_ptrstd::shared_ptr可以通过它们的默认构造函数构造为“空”指针:

......这意味着使用它们而不验证它们不是空的会导致崩溃,这正是J.卡马克的讨论的全部内容。

然后,我们有一个有趣的问题:“我们如何通过智能指针作为函数参数?”

Jon对C ++问题的answer - 将引用传递给boost :: shared_ptr ,以下注释表明即使这样,通过复制或引用传递智能指针也不会像人们想象的那样清晰(我喜欢自己的“通过引用“默认情况下,但我可能是错的)。


参考文献更清晰,更易于使用,并且他们在隐藏信息方面做得更好。 然而,参考文献不能重新分配。 如果您需要先指向一个对象,然后再指向另一个对象,则必须使用指针。 引用不能为空,所以如果存在有问题的对象可能为空的机会,则不得使用引用。 你必须使用一个指针。 如果你想自己处理对象操作,也就是说如果你想为堆上的对象分配内存空间而不是堆栈,你必须使用指针

int *pInt = new int; // allocates *pInt on the Heap

对于指针,你需要它们指向某些东西,所以指针会占用内存空间。

例如,一个采用整型指针的函数不会接受整型变量。 所以你需要为这个函数首先创建一个指针。

至于参考,它不会花费内存。 你有一个整型变量,你可以将它作为参考变量传递。 而已。 您无需专门为其创建参考变量。


我更喜欢使用指针。 At least it is clear what you are doing. I have the feeling that references are mostly used because of STL and its syntax implications on code. Because of that also so many C++ standard library novelties like std::move ..... to get exactly what you want, and not what you intuitively would have thought of.


我的经验法则是:

  • 使用传出或输入/输出参数的指针。 所以可以看出价值将会改变。 (您必须使用&
  • 如果NULL参数是可接受的值,则使用指针。 (如果它是传入参数,请确保它是const
  • 如果传入参数不能为NULL并且不是原始类型( const T& ),则使用传入参数的引用。
  • 返回新创建的对象时使用指针或智能指针。
  • 使用指针或智能指针作为结构或类成员而不是引用。
  • 使用引用来消除锯齿(例如int &current = someArray[i]

无论你使用哪一个,不要忘记记录你的功能和参数的含义,如果它们不明显。


无论您身在何处,都可以使用参考,指针。

直到你不能,避免指针。

原因是指针会使事情难以遵循/阅读,不太安全,而且操作远比其他任何构造更危险。

所以经验法则是只有在没有其他选择时才使用指针。

例如,返回一个指向对象的指针是一个有效的选项,当函数在某些情况下可以返回nullptr并且假定它会。 也就是说,更好的选择是使用类似boost::optional东西。

另一个例子是使用指向原始内存的指针来进行特定的内存操作。 这应该隐藏在代码的非常窄的部分中,以帮助限制整个代码库中的危险部分。

在你的例子中,使用指针作为参数没有意义,因为:

  1. 如果你提供nullptr作为参数,你将进入undefined-behavior-land;
  2. 参考属性版本不允许(不容易找到技巧)1的问题。
  3. 引用属性版本更易于理解用户:您必须提供有效的对象,而不是可能为null的对象。

如果函数的行为不得不使用或不使用给定的对象,那么使用指针作为属性表明你可以传递nullptr作为参数,并且对于函数来说是很好的。 这是用户和实施之间的合同。


这不是品味的问题。 这里有一些明确的规则。

如果你想在它声明的范围内引用一个静态声明的变量,那么使用C ++引用,它将是完全安全的。 这同样适用于静态声明的智能指针。 按引用传递参数就是这种用法的一个例子。

如果你想引用比范围更广泛的范围,那么你应该使用一个引用计数的智能指针,它是非常安全的。

你可以引用一个集合的元素来引用句法的方便,但这不是安全的; 该元素可以随时删除。

要安全地持有对集合元素的引用,您必须使用引用计数智能指针。





reference