【问题标题】:Member access differences会员访问差异
【发布时间】:2010-12-30 13:58:58
【问题描述】:

谁能告诉我(*ptr).fieldptr->field 有什么区别? 我知道它以某种方式连接到静态和动态链接,但我不知道它是什么。 谁能告诉我不同​​之处并举个例子?

编辑: 如果我有这个代码:

Point p;       //point is a class that derive from class shape 
Shape *s=&p; 
               //there is a diffrence if i write:

(*s).print();  //print is virtual func
s->print();    // the answers will not be the same, why?

TNX!

【问题讨论】:

  • 您的意思是(*ptr).field 还是*ptr.field?如果你的意思是*ptr.field,那么这两种形式根本没有任何共同之处,这使得这个问题几乎没有意义。
  • 根据您的编辑:答案是一样的。见下文。
  • 我不知道您在代码示例中所指的“答案”是什么,但在您的代码示例中,两个调用都会完全相同。它们之间绝对没有区别。
  • @aharont:您发布的代码没有说明差异。请发布一个完整的最小测试用例,它应该可以按原样编译。因此,它应该包括ShapePoint 的最少定义。它也应该是一个新问题,因为新问题不同于“(*ptr).field 和 ptr->field 之间有什么区别?”

标签: c++ operators operator-precedence


【解决方案1】:

这与静态或动态链接无关。

C++'s operator precedence. 的优先级低于*,因此*ptr.fldptr->fld 之间实际上存在很大差异。例如下面的代码演示:

#include <iostream>

struct foo {
  int f;
};

int main() {
  struct foo *p = new struct foo;
  p->f = 42;
  std::cout << p->f << std::endl;
  std::cout << (*p).f << std::endl;
  // The following will not compile
  // std::cout << *p.f << std::endl;
}

正如 John Knoeller 所指出的,ptr-&gt;fld(*(ptr)).fld 的语法糖,但与 *ptr.fld 不同,后者实际上会计算为 *(ptr.fld),可能不是您想要的。

当您有一个指向结构的指针并想要访问其中包含的字段时,您将使用ptr-&gt;fld(*(ptr)).fld 表示同样的意思,但没有那么整洁。当您有一个结构而不是指向结构的指针时,您将使用*strct.fld,其中包含一个字段 (fld),该字段是您要取消引用的指针。 ptr-&gt;fld 的情况如上图所示。 *strct.fld 的大小写可以与以下结构一起使用:

struct foo {
  int *fld;
}

struct foo f;
f.fld = new int;
*f.fld = 42;

【讨论】:

  • 正如 outis 所指出的,ptr->field 实际上是 (*(ptr)).field 的语法糖,而不是 (*ptr).field 的语法糖。哎呀。固定。
  • 因为ptr 是一个标识符,所以(*ptr).field 很好。不过,最好指出一般情况。
  • OP 确认他实际上是指(*ptr).field。我编辑了问题。
【解决方案2】:

它与静态或动态链接无关 两个表达式都会返回 ptr.field 的值

ptr->field 形式是一种缩写语法,用于直接从指针访问成员

更新:我想到您的原始意图不是链接而是绑定 如果这确实是您的目标,那么有 静态绑定动态绑定 这与 -> 运算符 see here

有一些关系

【讨论】:

  • 是的,但缩写为 (*ptr).fld,而不是 *ptr.field。
  • 重要的是要注意这些不一定是等价的,因为在 C++ 中可能会重载 operator* 和 operator->!
  • @Hostile Fork:这就是为什么在某些商店中,C++ 代码审查会议应该包括电击疗法机器。
  • 很高兴我们把它拼凑到最后 ;-)
【解决方案3】:

静态链接是链接器将程序中使用的所有库例程复制到可执行映像中的结果。与动态链接相比,这可能需要更多的磁盘空间和内存,但速度更快且更便携,因为它不需要在运行它的系统上存在库。

动态链接是通过将可共享库的名称放在可执行映像中来完成的。在运行映像之前,不会发生与库例程的实际链接,此时可执行文件和库都放置在内存中。动态链接的一个优点是多个程序可以共享库的一个副本。

但这与您提到的指针间接无关-实际上,这两个表达式是相同的。

【讨论】:

  • 不完全。 ptr->field 等同于 (*ptr).field,而不是 *ptr.field。
【解决方案4】:

我假设*ptr.field 是指(*ptr).field

就仅考虑内置运算符而言,两者之间没有区别。不,它与“静态”或“动态”链接无关,无论这些术语暗示什么。

两者之间唯一的潜在区别是在ptr-&gt;field 变体中-&gt; 是C++ 中的可重载运算符,而在(*ptr).field 变体中只有* 是可重载的,而. 不是。

此外,在非常古老的 C 语言 (CRM C) 版本中,这两种成员访问方法之间存在一些差异,但我怀疑今天是否有人关心这些。

【讨论】:

    【解决方案5】:

    这与链接无关。

    ptr->fld 
    

    只是

    的简写
    (*ptr).fld
    

    这是纯粹的语法糖;)

    【讨论】:

    • 具体参见 C++ 标准的第 5.2.5.3 节。
    • § 5.2.5 3 如果 E1 的类型为“指向类 X 的指针”,则表达式 E1->E2 被转换为等价形式 (*(E1)).E2; 5.2.5 的其余部分将仅处理第一个选项(点)。
    • @outis:您引用的部分描述了 内置 运算符-&gt; 的行为。不要忘记,C++ 中的-&gt; 不一定指的是内置运算符。它也可以由用户超载。 OP 没有说 ptr 是原始指针类型。
    • 我没有忘记这一点。由于引用指出当ptr 是指针时等效适用,所以我没有费心用我自己的话来说明。
    【解决方案6】:

    只要ptr 是一个指针,一旦正确括起来(正如其他人所说),两者就是等价的。如果ptr 是一个对象,而不是一个指针,那么它们可能会有所不同,具体取决于对象的类或祖先对operator*operator-&gt;(如果有)的定义。

    【讨论】:

    • 好的,如果我有这个代码:Point p;//point 是一个派生自类形状 Shape *s=&p; 的类如果我写 (*s).print();//print 是 s->print() 的虚拟函数,则存在差异;答案不会一样,为什么?
    【解决方案7】:

    表单 -> 只是取消引用指针和访问成员的简写。

    (*ptr).field;
    // Equiv to 
    ptr->field;
    

    使用 -> 的一个很好的理由是当你跟随一个链时:

    int x = (*(*(*(*ptr).field1).field2).field3).field4;
    // Equiv to 
    int y = ptr->field1->field2->field3->field4;
    

    第二个变得更具可读性。

    至于你问题的第二部分。
    我发现举个例子真的很容易。

    #include <iostream>
    
    class Shape
    {
        public:   virtual ~Shape()        {}
                  virtual void Print()    {std::cout << "Shape\n";}
    };
    class Point: public Shape
    {
        public:   virtual void Print()    {std::cout << "Point\n";}
    };
    
    int main ()
    {
        Point   p;
        Shape*  s = &p;
    
        s->Print();
        (*s).Print();
    }
    
    > vi x.cpp
    > g++ x.cpp
    > ./a.exe
    Point
    Point
    

    如您所见,两种情况下的结果是相同的。

    当您通过指针或引用调用方法时,将调用虚拟调用机制。星号运算符(AKA 取消引用运算符)返回对对象的引用(它实际上并不取消引用该对象)。所以当它被用来调用一个方法时,会调用虚调用机制,并调用该方法的最衍生版本。

    【讨论】:

    • 好吧,坦克,虽然我的博士。说有一些不同,但我不记得是什么......
    • (*ptr)->f1 和 ptr->f1 没有区别。这是由标准定义的。如果我们转向虚函数。有些情况下不会使用虚拟调用机制,但这不是其中之一。
    • 那么什么情况下不会使用virtal call机制呢?
    • 当你明确命名时(我忘记了这个词)。但是 Point 中的方法可以调用 Shape::Print() 或 Point::Print() 来获取该方法的特定版本。
    • 你在想“合格的名字”吗?或者也许是“显式范围解析”?
    【解决方案8】:

    它们都是相同的。
    *ptr.field 取消引用变量 ptr 然后返回成员 field 的值。

    -&gt; 运算符是上述的简写符号。

    【讨论】:

    • 实际上,*ptr.field 被评估为*(ptr.field),而不是(*ptr).field
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-18
    • 2016-06-08
    • 1970-01-01
    • 1970-01-01
    • 2014-05-27
    相关资源
    最近更新 更多