【问题标题】:Pointer-to-member: what does the pointer value represent?指向成员的指针:指针值代表什么?
【发布时间】:2012-08-20 01:52:09
【问题描述】:

我正在玩指向成员的指针,并决定实际打印指针的值。结果出乎我的意料。

#include <iostream>

struct ManyIntegers {

    int a,b,c,d;
};

int main () {

    int ManyIntegers::* p;

    p = &ManyIntegers::a;
    std::cout << "p = &ManyIntegers::a = " << p << std::endl; // prints 1

    p = &ManyIntegers::b;
    std::cout << "p = &ManyIntegers::b = " << p << std::endl; // prints 1

    p = &ManyIntegers::c;
    std::cout << "p = &ManyIntegers::c = " << p << std::endl; // prints 1

    p = &ManyIntegers::d;
    std::cout << "p = &ManyIntegers::d = " << p << std::endl; // prints 1

    return 0;
}

为什么p 的值总是1? p 的值不应该以某种方式反映它指向的类成员吗?

【问题讨论】:

    标签: c++ pointers


    【解决方案1】:

    正如每个人所说,ostream 没有定义适当的operator&lt;&lt;

    试试这个:

    #include <cstddef>
    #include <iostream>
    
    struct Dumper {
      unsigned char *p;
      std::size_t size;
      template<class T>
      Dumper(const T& t) : p((unsigned char*)&t), size(sizeof t) { }
      friend std::ostream& operator<<(std::ostream& os, const Dumper& d) {
        for(std::size_t i = 0; i < d.size; i++) {
          os << "0x" << std::hex << (unsigned int)d.p[i] << " ";
        }
        return os;
      }
    };
    
    #include <iostream>
    
    struct ManyIntegers {
    
        int a,b,c,d;
    };
    
    int main () {
    
        int ManyIntegers::* p;
    
        p = &ManyIntegers::a;
        std::cout << "p = &ManyIntegers::a = " << Dumper(p) << "\n"; 
    
        p = &ManyIntegers::b;
        std::cout << "p = &ManyIntegers::b = " << Dumper(p) << "\n"; 
    
        p = &ManyIntegers::c;
        std::cout << "p = &ManyIntegers::c = " << Dumper(p) << "\n"; 
    
        p = &ManyIntegers::d;
        std::cout << "p = &ManyIntegers::d = " << Dumper(p) << "\n"; 
    
        return 0;
    }
    

    【讨论】:

    • 这很强大。 “联合转换”also works,但我不确定是否存在保证至少是指向成员的指针大小的数字类型。
    • @MagnusHoff 没有保证数字大小,但是对于指向数据成员的指针,假设大小将小于或等于 size_t 的大小是相当安全的,因为唯一需要的信息是偏移量。另一方面,对于指向成员函数的指针,问题更复杂,在 64 位机器上,如果它们是 16 字节并且比任何整数类型都长,我不会感到惊讶。
    • 我喜欢它。我自己的实现一直使用一个类模板,一个函数模板返回它以从类型推导中受益;这是实现相同目的的更简单的方法。请注意,只有当Dumper 是临时的,它才真正起作用;否则,如果您正在转储临时对象,那么您将遇到对象问题的生命周期。 (这在实践中不是问题,但如果您将其实例放入工具箱中,则值得记录。)
    【解决方案2】:

    标准 ostream operator&lt;&lt; 没有指向成员的指针重载,因此您的指针已隐式转换为 bool

    【讨论】:

    • 好吧,如果我改为使用int* p = new int(5);,那么 std::cout
    • reinterpret_cast&lt;unsigned&gt;(p) triggers compile errors.
    • @curvative 我的意思是指向不同类型成员的指针,为简洁起见略过。很抱歉给您带来不便。
    • 为什么会有 4 个赞?提供的解决方案不起作用!
    • 这合法吗?我在标准中找不到任何允许使用 reinterpret_cast 将指针从成员转换为整数类型的内容。我知道合法地这样做的唯一方法是*reinterpret_cast&lt;unsigned*&gt;(&amp;p),其中unsigned 应替换为适当大小的整数类型。
    【解决方案3】:

    p 实际上包含对象中的偏移量。打印它们会打印隐式转换的bool 值 true 或 false,如果它们确实分别包含一些偏移量。发生转换是因为ostream 的插入成员对指向成员的指针没有任何重载。

    【讨论】:

    • p 实际包含的内容未指定。所指定的只是在不同类型的表达式中使用p 的结果。 (在最常用的实现技术中,p 包含偏移量 + 1,因此指向成员的空指针可以全为 0 位。)
    • 我同意你的看法,尽管实际上大多数编译器都包含你所说的偏移量:)
    • 不管怎样,ideone 的指向成员的空指针似乎都将所有位设置为 1:ideone.com/jpwXP
    • @MagnusHoff 这可能是我会采用的解决方案:-)。但这不是我在现有编译器中看到的解决方案(g++ 和 VC++,我认为:我知道我已经看过一两个,但我不再 100% 确定哪个)。
    【解决方案4】:

    operator&lt;&lt; 没有重载,它将指向成员的指针作为参数。因此,如果您尝试打印指向成员的指针,它隐式转换为true,后者被传递给以bool 作为参数的重载,并打印1 对应于true .

    如果你使用std::boolalpha流操纵器,它将打印true而不是1

    std::cout << std::boolalpha << "p = &ManyIntegers::a = " << p ;
               //^^^^^^^^^^^^^^
    

    输出(见ideone):

    p = &ManyIntegers::a = true

    【讨论】:

      【解决方案5】:

      成员指针不一定是数值,通常它是结构或类似的东西。我不认为有办法从成员指针中获取值,但即使有,我也不知道这会有什么用。

      【讨论】:

        【解决方案6】:

        这是一个完全符合标准的实现,用于显示指向成员的指针的内存表示:

        #include <iostream>
        #include <iomanip>
        
        template<int... I> struct index_tuple { using succ = index_tuple<I..., sizeof...(I)>; };
        template<int I> struct indexer { using type = typename indexer<I - 1>::type::succ; };
        template<> struct indexer<0> { using type = index_tuple<>; };
        template<typename T> typename indexer<sizeof(T)>::type index(const T &) { return {}; }
        
        template<typename T> class dumper {
            unsigned char buf[sizeof(T)];
            friend std::ostream &operator<<(std::ostream &os, const dumper &o) {
                std::ios_base::fmtflags flags{os.flags()};
                std::copy_n(o.buf, sizeof(T),
                    std::ostream_iterator<int>(os << std::hex << std::showbase, " "));
                return os << std::setiosflags(flags);
            }
            template<int... I> dumper (const T &t, index_tuple<I...>):
                buf{reinterpret_cast<const unsigned char *>(&t)[I]...} {}
        public:
            dumper(const T &t): dumper(t, index(t)) {}
        };
        template<typename T> dumper<T> dump(const T &t) { return {t}; }
        
        struct ManyIntegers {
            int a,b,c,d;
        };
        
        int main () {
            std::cout << "p = &ManyIntegers::a = " << dump(&ManyIntegers::a) << std::endl;
            std::cout << "p = &ManyIntegers::b = " << dump(&ManyIntegers::b) << std::endl;
            std::cout << "p = &ManyIntegers::c = " << dump(&ManyIntegers::c) << std::endl;
            std::cout << "p = &ManyIntegers::d = " << dump(&ManyIntegers::d) << std::endl;
        }
        

        输出如预期:

        p = &ManyIntegers::a = 0 0 0 0 
        p = &ManyIntegers::b = 0x4 0 0 0 
        p = &ManyIntegers::c = 0x8 0 0 0 
        p = &ManyIntegers::d = 0xc 0 0 0 
        

        【讨论】:

        • 我想这需要一些解释。你能用几句话解释一下你在那里做什么吗?
        • @BjörnPollex 我正在将成员指针的内存内容复制到unsigned char 缓冲区中,以便将其打印出来。索引技巧只是为了有效地执行复制。
        猜你喜欢
        • 2018-03-24
        • 2023-03-12
        • 2012-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-03
        • 2013-05-02
        • 1970-01-01
        相关资源
        最近更新 更多