2011-07-05 2 views
47

Да, я видел this question и this FAQ (неправильная ссылка) this FAQ, но я до сих пор не понимают, что ->* и .* означает в C++.
Эти страницы предоставляют информацию о операторах (таких как перегрузка), но, похоже, не очень хорошо объясняют, что они являются.Что такое указатель-член-> * и. * Операторы в C++?

Что такое ->* и .* в C++, и когда вам нужно использовать их по сравнению с -> и .?

+3

Я думаю, что [этот FAQ] (http://www.parashift.com/c++-faq-lite/pointers-to-members.html) объясняет это довольно хорошо. –

+0

@ Rob: Не могу поверить, что я разместил неверную ссылку. X___X - это тот, который я хотел опубликовать, а не MSDN! - Я посмотрел на него, но я ничего не понял. Все, что они говорят, это то, что «тип отличается», но я нашел такое путаное ... – Mehrdad

ответ

50

Я надеюсь, что этот пример будет убрать вещи для вас

//we have a class 
struct X 
{ 
    void f() {} 
    void g() {} 
}; 

typedef void (X::*pointer)(); 
//ok, let's take a pointer and assign f to it. 
pointer somePointer = &X::f; 
//now I want to call somePointer. But for that, I need an object 
X x; 
//now I call the member function on x like this 
(x.*somePointer)(); //will call x.f() 
//now, suppose x is not an object but a pointer to object 
X* px = new X; 
//I want to call the memfun pointer on px. I use ->* 
(px ->* somePointer)(); //will call px->f(); 

Теперь, вы не можете использовать x.somePointer() или px->somePointer(), потому что нет такого члена в классе X. Для этого специальной функции члена синтаксиса вызова указателя используется ... просто попробуйте несколько примеров самостоятельно, вы привыкнете к нему

+2

Добавьте ** обязательные ** дополнительные привязки, чтобы компенсировать приоритет оператора. –

+2

@Armen: О, я вижу, что происходит сейчас ... Я никогда раньше не видел и не нуждался в чем-то подобном. Это круто (+1), но, я думаю, теперь вопрос: как «указатель» отличается от обычного указателя на функцию, для которого требуется другой синтаксис? (например, больше?) – Mehrdad

+0

@Mehrdad: Дело в том, что для вызова нормальной функции вы просто предоставляете аргументы. Но в случае нестатических функций-членов вам также нужен объект, на который вы хотите вызвать функцию. Отсюда новый синтаксис. –

10

В двух словах: вы используете -> и ., если знаете, к чему вы хотите получить доступ. И вы используете ->* и .*, если вы не знаете, к кому вы хотите получить доступ.

Пример с простым навязчивым списком

template<typename ItemType> 
struct List { 
    List(ItemType *head, ItemType * ItemType::*nextMemPointer) 
    :m_head(head), m_nextMemPointer(nextMemPointer) { } 

    void addHead(ItemType *item) { 
    (item ->* m_nextMemPointer) = m_head; 
    m_head = item; 
    } 

private: 
    ItemType *m_head; 

    // this stores the member pointer denoting the 
    // "next" pointer of an item 
    ItemType * ItemType::*m_nextMemPointer; 
}; 
+4

+1 для первого предложения, хотя в моей жизни я никогда не знал, какой член я хочу получить, ха-ха. :) – Mehrdad

6

Так называемыми «указателями» для членов в C++ больше похожи смещениями, внутренне. Вам нужен и такой «указатель» члена, и объект, чтобы ссылаться на элемент в объекте. Но члены «указатели» используются с синтаксисом указателя, отсюда и название.

Существует два способа иметь объект под рукой: у вас есть ссылка на объект или указатель на объект.

Для справки используйте .*, чтобы объединить его с указателем на элемент, а для указателя используйте ->*, чтобы объединить его с указателем на элемент.

Однако, как правило, не используйте указатели элементов, если вы можете избежать этого.

Они подчиняются довольно контр-интуитивным правилам, и они позволяют обойти protected доступ без какого-либо явного литья, то есть непреднамеренно & hellip;

Приветствия & НТН,

+0

+1 для объяснения этого без кода. :) Вопрос: Почему мы не можем просто взять адрес функции, как функция нормальной функции? Является ли указатель на функцию-член отличным от указателя на какую-либо другую функцию? (например, больше?) – Mehrdad

+1

@Mehrdad: Если бы у вас мог быть указатель на функцию-член, которая была ограничена не виртуальными функциями-членами, то это действительно мог бы быть адрес. Однако виртуальная или нет не является частью типа указателя функции-члена. И поэтому его представление должно содержать некоторую информацию о том, относится ли текущее значение к виртуальной функции или нет, а если виртуально, для информации реализации на основе vtable, которая определяет смещение в таблице vtable класса, с которой связан тип указателя. –

4

Вы не можете разыменования указателя членов в качестве обычных указателей -., Поскольку функции-члены требуют this указатель, и вы должны передать его как-то. Таким образом, вам нужно использовать эти два оператора, с объектом с одной стороны, и указатель на другом, например. (object.*ptr)().

Рассмотрите возможность использования function и bind (std:: или boost::, в зависимости от того написать ли вы C++ 03 или 0x) вместо тех, хотя.

+0

Я думаю, что это может быть лучшим объяснением здесь. – Marcin

18

EDIT: Кстати, это странно для virtual member functions pointers.

Для переменных-членов:

struct Foo { 
    int a; 
    int b; 
}; 


int main() 
{ 
    Foo foo; 
    int (Foo :: * ptr); 

    ptr = & Foo :: a; 
    foo .*ptr = 123; // foo.a = 123; 

    ptr = & Foo :: b; 
    foo .*ptr = 234; // foo.b = 234; 
} 

функции-члены практически одинаковы.

struct Foo { 
    int a(); 
    int b(); 
}; 


int main() 
{ 
    Foo foo; 
    int (Foo :: * ptr)(); 

    ptr = & Foo :: a; 
    (foo .*ptr)(); // foo.a(); 

    ptr = & Foo :: b; 
    (foo .*ptr)(); // foo.b(); 
} 
+2

+1 для отображения того, что синтаксис применяется ко всем членам, а не только к функциям-членам. Я нахожу, что указатели на переменные-члены очень редко используются, хотя, несмотря на их многие довольно интересные потенциальные приложения. –

5

Если у вас есть обычный указатель (для объекта или базового типа), вы должны использовать * разыменовать ему:

int a; 
int* b = a; 
*b = 5;  // we use *b to dereference b, to access the thing it points to 

Концептуально, мы делаем то же самое с членом указатель функции:

class SomeClass 
{ 
    public: void func() {} 
}; 

// typedefs make function pointers much easier. 
// this is a pointer to a member function of SomeClass, which takes no parameters and returns void 
typedef void (SomeClass::*memfunc)(); 

memfunc myPointer = &SomeClass::func; 

SomeClass foo; 

// to call func(), we could do: 
foo.func(); 

// to call func() using our pointer, we need to dereference the pointer: 
foo.*myPointer(); 
// this is conceptually just: foo . *myPointer (); 


// likewise with a pointer to the object itself: 
SomeClass* p = new SomeClass; 

// normal call func() 
p->func(); 

// calling func() by dereferencing our pointer: 
p->*myPointer(); 
// this is conceptually just: p -> *myPointer (); 

Я надеюсь, что это поможет объяснить концепцию. Мы фактически разыскиваем наш указатель на функцию-член. Это немного сложнее, чем это - это не абсолютный указатель на функцию в памяти, а только смещение, которое применяется к foo или p выше. Но концептуально мы разыгрываем его, так же, как мы бы разыменовали обычный указатель объекта.