2011-07-29 27 views
7

Giả sử rằng chúng ta có một thành viên trỏ đến lớp chỉ vào một trường của một lớp. Chúng ta cũng có một con trỏ tới trường cụ thể đó trong một cá thể cụ thể của lớp. Ví dụ, chúng ta có thể có một cái gì đó như thế này:Phục hồi phụ huynh từ Pointer-to-member

class A { 
    B inner_object; 
} 

A* myA = /* ... */ 
B* ptr = &myA->inner_object; 
B A::* memPtr = &A::inner_object; 

Có cách nào để sử dụng ptrmemPtr để phục hồi myA? Tức là, nếu chúng ta chưa có con trỏ rõ ràng cho myA, chúng ta có thể tạo một trong số ptrmemPtr không?

+0

Bạn có thể rõ ràng hơn không. Tôi không hiểu bạn muốn làm gì. Lấy làm tiếc. – Mahesh

Trả lời

4

Sau một số lượng nghiên cứu phong nha ...

Điều này thực sự được thực hiện trong hầu hết các triển khai danh sách xâm nhập công nghiệp. Nó đòi hỏi một số hackery, tuy nhiên.

Boost cấu trúc xâm nhập sử dụng sau đây (và có, nó là thực hiện cụ thể)

template<class Parent, class Member> 
inline const Parent *parent_from_member(const Member *member, const Member Parent::* ptr_to_member) 
{ 
    return (const Parent*)((const char*)member - 
     offset_from_pointer_to_member(ptr_to_member)); 
} 


template<class Parent, class Member> 
inline std::ptrdiff_t offset_from_pointer_to_member(const Member Parent::* ptr_to_member) 
{ 
    //The implementation of a pointer to member is compiler dependent. 
    #if defined(BOOST_INTRUSIVE_MSVC_COMPLIANT_PTR_TO_MEMBER) 
    //msvc compliant compilers use their the first 32 bits as offset (even in 64 bit mode) 
    return *(const boost::int32_t*)(void*)&ptr_to_member; 
    //This works with gcc, msvc, ac++, ibmcpp 
    #elif defined(__GNUC__) || defined(__HP_aCC) || defined(BOOST_INTEL) || \ 
    defined(__IBMCPP__) || defined(__DECCXX) 
    const Parent * const parent = 0; 
    const char *const member = reinterpret_cast<const char*>(&(parent->*ptr_to_member)); 
    return std::ptrdiff_t(member - reinterpret_cast<const char*>(parent)); 
    #else 
    //This is the traditional C-front approach: __MWERKS__, __DMC__, __SUNPRO_CC 
    return (*(const std::ptrdiff_t*)(void*)&ptr_to_member) - 1; 
    #endif 
} 

Về cơ bản điều tương tự (mặc dù trong C) như được thực hiện trong kernel linux để quản lý danh sách xâm nhập, với container_of macro (nhưng tất nhiên ptr-to-thành viên không được sử dụng):

#define container_of(ptr, type, member) ({ \ 
      const typeof(((type *)0)->member) *__mptr = (ptr); 
      (type *)((char *)__mptr - offsetof(type,member));}) 
3

Bạn không. Một con trỏ tới thành viên không có kiến ​​thức về bất kỳ cá thể nào của lớp mà nó là thành viên của. Đó là lý do tại sao bạn cần một phiên bản bất cứ lúc nào bạn muốn truy cập thành viên thông qua con trỏ.

Con trỏ tới thành viên là không con trỏ. Thật vậy, nó có lẽ là một sai lầm trên phần của ủy ban C++ thậm chí còn gọi nó là một con trỏ. Trên nhiều (nếu không phải là hầu hết) triển khai, ngay cả các kích thước của một con trỏ không bằng kích thước của một con trỏ đến thành viên. Không có thủ thuật bù đắp bạn có thể chơi ở đây. Và ngay cả khi bạn tìm thấy một cách, nếu bạn phân tích dữ liệu trong một con trỏ thành viên, nó vẫn sẽ được cụ thể cho việc thực hiện đó.

+1

Tôi nghĩ câu hỏi ngụ ý là: chúng ta có thể ví dụ: trừ một từ khác để có được một con trỏ đến đối tượng? –

+0

Ok, nhưng nếu chúng ta nghĩ rằng ptr-to-thành viên chỉ đơn giản là một bù đắp, cha mẹ nên được trivially phục hồi. Bạn đang nói một ptr-to-thành viên không phải là một bù đắp, hoặc không thể được thực hiện để hành xử như một? (EDIT: những gì oli nói) – bcr

+1

@Oli - tại sao cung cấp cho mọi người ý tưởng điên? :-) – littleadv

1

Bạn không thể.

Con trỏ tới thành viên không lưu trữ thông tin về bất kỳ cá thể cụ thể nào.

Nó chỉ biết một loại và con trỏ đến hàm trong loại đó.

1

này chắc chắn không phải là tiêu chuẩn và không thực sự khuyến khích cho sử dụng thực tế, nhưng bạn có thể thử điều này:

A *fake_A= reinterpret_cast<A *>(1); 
B *fake_B= &(fake_A->*ptr_to_member); 
char *fake_A_raw= static_cast<char *>(static_cast<void *>(fake_A)); 
char *fake_B_raw= static_cast<char *>(static_cast<void *>(fake_B)); 

ptrdiff_t offset_to_A_from_B= fake_B - fake_A; 

char *member_raw= static_cast<char *>(static_cast<void *>(member)); 
char *base_raw= member_raw - offset_to_A_from_B; 
A *base= static_cast<A *>(static_cast<void *>(base_raw)); 

Và bạn thực sự không nên làm điều này.

+0

và tại sao bạn đăng bài này? : P – YeenFei

+0

Haha có, đó là một ví dụ khá tuyệt vời. Nhưng có lẽ có những cách dễ dàng hơn để thực hiện cùng một điều với những gì chúng tôi có sẵn, bạn có nghĩ vậy không? Ví dụ: nếu bạn có một con trỏ tới một số cấu trúc loại A, bạn chỉ có thể lấy giá trị bù trừ từ việc trừ con trỏ tới trường mong muốn từ con trỏ đến cấu trúc. Hay tôi đang thiếu một cái gì đó? – bcr

+0

@bcr, đúng, nhưng đó không phải là những gì bạn yêu cầu. Bạn muốn biết được đưa ra một con trỏ đến thành viên làm thế nào để có được trở lại lớp gốc; bạn không thể đưa con trỏ đến thành viên để bù đắp trực tiếp. Ví dụ, con trỏ null cho thành viên không phải là bitwise zero. – MSN

0

Nó sẽ có thể và rất hữu ích. Con trỏ đến thành viên chỉ là một bù đắp miễn là bạn chắc chắn về loại cấu trúc nào chứa con trỏ tới thành viên.

Các vấn đề liên quan