В этой теме мной будет применяться авторский термин: мнимый указатель. Этот термин не технический, а используется только потому что то, что должно бы быть по логике указателем, и не указатель вовсе. Указатели, которые указывают на данные класса: на функции объекта или на переменные, хранимые внутри объекта, — указателями и не являются. Возможно, кому-то приходило в голову создать указатель на функцию внутри класса, а сообразить не удавалось, как такое провернуть.
Использовать обычный указатель для указания на части класса нельзя
Ради интереса можете попробовать создать простой класс и объявить и направить указатель на внутренности объявленного класса:
C++
1
2
3
4
5
6
7
8
9
classMyClass{
public:
intx;//<-- объявите в main указатель на x
voidfoo(){}//<-- объявите в main указатель на foo()
};
intmain(){
}
Не просто объявите указатели, но и обратитесь по ним к связанным с ними элементам, т. е. и к x и к foo(). Это возможно.
Ради тех, кому интересно попробовать, описание прячется под спойлер:
Создаётся этот мнимый указатель по достаточно простому, но сложночитаемому принципу:
C++
1
2
3
4
5
6
7
8
classMyClass{
public:
intx;
};
intmain(){
intMyClass::*ptr_x;
}
Как вы можете заметить, тип у мнимого указателя такой же, как тип у сущности, на которую он указывает: тип int. Здесь формула обычная: типы должны быть совместимы. А имя мнимому указателю даётся с явным указанием на класс, из которого буквально вытаскивается то, на что этот мнимый указатель должен показывать. Другими словами, используется операция разрешения видимости ::, после которой пишется имя указателю, в нашем случае ptr_x, и операцией * обозначается, что ptr_x должно быть указателем. Простыми словами: здесь указатель объявляется как самый обычный указатель, но с явным предписанием к нему области видимости по принадлежности к классу.
Надеюсь, что тот, кому сложно понять код, поймёт по объяснению, а тот, кому сложно понять объяснение, поймёт этот момент по коду.
Мало просто объявить указатель, надо уметь его и использовать. Коли уж некое подобие указателя есть, то пусть оно что-то полезное делает, не так ли? Обычным разыменованием тут не получится. Требуется иной подход.
Указатель, указывающий на "кишки класса, указателем не является, а является смещением относительно начала адреса объекта класса
У любого объекта есть начальный адрес, правильно? Объекты часто занимают больше одной ячейки памяти. По этим ячейкам можно прыгать. Такие прыжки, о которых сейчас идёт речь, называют смещениями. Вот, наш мнимый указатель — это просто смещение. Такой принцип смещений немного напоминает арифметику указателей, только в данном случае смещения идут совершенно иначе.
Чтобы уметь работать со мнимым указателем и обращаться к его конечным сущностям, т. е. к элементам, на которые такой указатель указывает, нужно чтобы существовал объект, элементы которого и будут нам нужны:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Листинг #1
#include <iostream>
usingnamespacestd;
classMyClass{
public:
intx;
};
intmain(){
MyClass obj;//Создание объекта
intMyClass::*ptr_x;
(obj.*ptr_x)=200;//Здесь можно увидеть, что это совсем не указатель
cout<<obj.x<<'\n';//И к тому же даёт не тот эффект, которого можно было бы ожидать
}
Листинг #1 показывает, что использование мнимого указателя требует повышенного внимания и может легко запутать даже в самом простом варианте. Этот же код можно написать следующим образом:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Листинг #2.1
#include <iostream>
usingnamespacestd;
classMyClass{
public:
intx;
};
intmain(){
MyClass obj;//Создание объекта
intMyClass::*ptr_x;//Объявление мнимого указателя
ptr_x=&MyClass::x;//Навязывание мнимому указателю смещения к x
(obj.*ptr_x)();//Вызов функции, посредством мнимого указателя
}
Чтобы понять пример #3, нужно обязательно иметь представление об обычных указателях на функции. Но суть его точно такая же, как в #2. Строится всё это по одному и тому же принципу: достаточно понять пример с обычной x внутри класса.
Я не имею никакого опыта в программировании, но мне ведь можно иметь свою точку зрения? То, что это напоминает выворачивание кишок наружу из здорового организма, способствует моему пониманию: всё это плохо! Конечно кто-то такое проделывает, но многие опытные программисты пишут о том, что за многие годы практики никогда не использовали этот приём.
У этого приёма много минусов: трудно читать, легко ошибиться, нужно очень серьёзное внимание к написанию кода и особое внимание к пониманию того, что делаешь. Это только те минусы, которые я сумел вывести в процессе написания текущей статьи, минусы наверняка ещё есть. И похоже, что количество минусов перевешивает количество плюсов, плюсы найти вообще очень трудно, поэтому могу посоветовать никогда этого не использовать, а просто знать, что язык С++ позволяет такое творить.
Мне остаётся надеяться, что этот материал кому-то будет интересен, окажется полезным и время на написание этой статьи было потрачено мной не зря. Но учтите, что ограничиваться этой страницей не стоит, нужно обязательно смотреть, что другие люди об этом всём пишут. Так ваша картина мира будет более полной.
Объявить внутри класса указатель на функцию-член класса и использовать его можно так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
usingstd::cout;
classMyClass{
public:
voidf1(){
cout<<"f1";
}
void(MyClass::*ptr)();//создаём указатель ptr на функцию-член MyClass
};
intmain(){
MyClassm;//создаём объект
m.ptr=&MyClass::f1;//направляем указатель объекта на функцию-член объекта
(m.*m.ptr)();//вызываем функцию-член объекта через указатель
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
usingstd::cout;
classMyClass{
public:
intf1(intvalue){
returnvalue;
}
int(MyClass::*ptr)(int);//создаём указатель ptr на функцию-член MyClass
};
intmain(){
MyClassm;//создаём объект
m.ptr=&MyClass::f1;//направляем указатель объекта на функцию-член объекта
cout<<(m.*m.ptr)(100);//вызываем функцию-член объекта через указатель
Добавить комментарий