代码之家  ›  专栏  ›  技术社区  ›  randomeval

&c::c和&(c::c)有什么区别?

c++
  •  47
  • randomeval  · 技术社区  · 6 年前

    测试代码在下面,我把输出信息放在注释中。 我用的是GCC4.8.5和CentOS7.2。

    #include <iostream>
    #include <cstdio>
    
    class C 
    {
        public:
            void foo() {
                printf("%p, %p\n", &C::c, &(C::c)); // output value is 0x4, 0x7ffc2e7f52e8
                std::cout << &C::c << std::endl;    // output value is 1
            }
            int a;
            int c;
    };
    
    int main(void)
    {
        C co;
    
        printf("%p\n", &C::c);              // output value is 0x4
        std::cout << &C::c << std::endl;    // output value is 1
    
    //    printf("%p\n", &(C::c));   // compile error, invalid use of non-static data member 'C::c'
    
        co.foo();
    
        return 0;
    }
    
    1. 根据 C++ operator Precedence 这个 :: 运算符的优先级高于 & 接线员。我想 &C::c 等于 &(C::c) ,但结果却相反。它们为什么不同?
    2. &(C::C) 在main中导致编译错误,但在 foo 为什么?
    3. 价值 &C:C 是不同的 printf std::cout ,为什么?
    4 回复  |  直到 6 年前
        1
  •  46
  •   melpomene    6 年前

    C++将两种操作数区分为 & 运算符、一般左值和(限定的)特定标识符。在 &C::c 操作数 & 是一个限定标识符(即,只是一个名称),而在 &(C::c) 操作数是一个通用表达式(因为 ( 不能是名称的一部分)。

    限定标识符表单有一个特殊情况:如果它引用类的非静态成员(如 C::c ) & 返回一个称为“指向C成员的指针”的特殊值。见 here for more information about member pointers .

    &(C::C) 没有特例。 C:C 由于没有对象可以获取 c 的成员。至少那是发生在 main ;方法 C (就像你的 foo )有一个隐含的 this 反对,所以 C:C 实际上是指 this->c 那里。

    至于为什么 printf VS cout :当您尝试使用 << ,它隐式转换为 bool ,屈服 false 如果是空指针 true 否则。 打印为 0 ; 打印为 1 . 你的成员指针不是空的,所以你得到 . 这与普通指针不同,后者隐式转换为 void * 并打印为地址,但成员指针不能转换为 空洞* 所以唯一适用的过载 operator<< 是为了 布尔 . 见 https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt#Notes .

    请注意,从技术上讲 普林特 调用具有未定义的行为。 %p 拿一个 空洞* 你传递不同类型的指针。在正常函数中调用 T * 空洞* 会很高兴的,但是 普林特 是一个变量参数函数,它不向其参数列表提供类型上下文,因此需要手动转换:

    printf("%p\n", static_cast<void *>(&(C::c)));
    

    标准的相关部分是 [一元表达式] ,说:

    一元的结果 & 运算符是指向其操作数的指针。 操作数应为左值或 合格身份证 . 如果操作数是 合格身份证 命名非静态或变量成员 m 某个阶级的 C 带类型 T ,结果具有指向类成员的类型指针 C 类型 T –是一个prvalue,表示 C​::​m . 否则,如果表达式的类型是 T ,结果具有指向 T -[…]

        2
  •  4
  •   Jans    6 年前

    而表达式 &C::c 导致指向成员的指针 c 表达 &(C::c) 产生成员变量的地址 C . 你在输出中看到的区别是 std::cout 包含一个bool隐式转换,它告诉您指针是否为空。

    自从 &C:C 实际上不是空的隐式转换为 bool 有价值的 true 1

        3
  •  1
  •   M.M    6 年前

    Q1: 它的语法有特殊的意义 & 接着是一个未被征服的 合格身份证 . 它的意思是形成一个指向成员的指针。此外,没有其他方法可以形成指向成员的指针。这是由C++ 17 [ ExpR.unal.op] / 4所涵盖的:

    只有当显式 & 使用,其操作数为 合格身份证 不在括号内。[注:即表达式 &(qualified-id) ,其中 合格身份证 括在括号中,不形成指向成员的指针类型的表达式。也不是 合格身份证 […]


    Q3: 在这两种情况下 printf("%p\n", &C::c); , &C::c 是指向成员的指针。这个 %p 格式说明符仅用于 void * 所以这会导致未定义的行为,并且程序输出没有意义。

    守则 cout << &C::c; 通过 operator<<(bool val) ,因为存在从指针到成员到 bool (有结果) true 在所有情况下),请参见[conv.bool]/1。

    为了进一步讨论如何打印指向成员的指针, see this answer .


    Q2: 守则 &(C::c) 不构成指向上述成员的指针。

    现在,密码 C::c 属于语法范畴 ID表达式 . (即 合格身份证 不合格身份证 )。安 ID表达式 对其使用有一些限制,[expr.prim.id]/1:

    只能使用表示类的非静态数据成员或非静态成员函数的ID表达式:

    • 作为类成员访问的一部分,其中对象表达式引用成员的类或从该类派生的类,或者
    • 形成指向成员(7.6.2.1)的指针,或
    • 如果该id表达式表示非静态数据成员,并且它出现在未计算的操作数中。

    当我们在里面的时候 C::foo 函数,第一个要点适用。代码与 &c 但有不必要的条件。这个有类型 int * . 你可以输出这个 std::cout << &(C::c); 它将显示内存地址 this->c .

    当我们在 main 函数,三个项目符号都不适用,因此 &(C::C) 是病态的。

        4
  •  0
  •   shawn    6 年前

    首先,您不能访问 int c 通过使用 &(C::c) 在教室外面。 &(C::C) 指的是 c 实例 C “,有点 &(this->c) 在这里。不管你的 C 不是类的静态成员 C 而且没有 C 实例。您无法访问 int x = C::c 也在外面。

    所以你看到一个错误:

    //    printf("%p\n", &(C::c));   // compile error, invalid use of non-static data member 'C::c'
    

    如果你有 static int c 然后 C::c 在类之外是可以的,因为这里不需要实例。

    我们跑吧

    #include <iostream>
    #include <cstdio>
    
    class C
    {
        public:
            void foo() {
                printf("%p, %p, this=%p\n", &C::c, &(C::c), this);
            }
            int a;
            int c;
    };
    
    int main(void)
    {
        C co;
        co.foo();
        return 0;
    }
    

    输出为:

    0x4, 0x7ffee78e47f4, this=0x7ffee78e47f0
    // 0x4 + this == 0x7ffee78e47f4
    // see reference
    

    为了 std::out : << &C::c 含蓄地 bool 如此 true 1 你看到了。你可以请客 &C::c 作为 an offset of c in C ,如果没有 C 实例。

    这就是全部。

    一些参考: C++: Pointer to class data member "::*"

    完整描述: https://en.cppreference.com/w/cpp/language/pointer