Форум программистов, компьютерный форум, киберфорум
Наши страницы

непонятная конструкция, шаблоны - C++

Войти
Регистрация
Восстановить пароль
Другие темы раздела
C++ Структура отрезок. http://www.cyberforum.ru/cpp-beginners/thread337576.html
Как сделать правильней и красивее на ваш взгляд - struct tPoint { int x, y; }; struct tSegment { tPoint pt1, pt2; };
C++ Аргументы функции Вопрос на счет аргументов функции в С++. есть такая функция: void Teleport(Player* pPlayer, float array) { int i = 0; pPlayer->TeleportTo(massive, massive, massive, massive,... http://www.cyberforum.ru/cpp-beginners/thread337569.html
C++ Ошибка ввода
После введения названия книги программа аварийно завершается. В чем проблема ? #include<iostream> #include<windows.h> #include<stdlib.h> using namespace std; class publications {protected:...
Непонятка с заголовочными файлами C++
game.h #ifndef _GAME_H_ #define _GAME_H_ #include <gl\gl.h> #include <gl\glu.h> #pragma comment (lib, "opengl32.lib") #pragma comment (lib, "glu32.lib")
C++ Программа, которая переводит числа в слова http://www.cyberforum.ru/cpp-beginners/thread337519.html
Здрасти всем. Есть код: #include <conio.h> #include <stdio.h> #include <iostream> #include <stdlib.h> string NumericToString(int n)
C++ Указатели в C++. Я никак не могу понять чем отличаются следующие объявления: int const * pTemp; const int * pTemp; И вот еще что не понятно: const char = "Some string" char const = "Some other string"; ... подробнее

Показать сообщение отдельно
ValeryLaptev
Эксперт С++
1046 / 825 / 48
Регистрация: 30.04.2011
Сообщений: 1,659
30.07.2011, 17:48
LosAngeles, смотри сюда:


Дело в том, что указатель на метод существенно отличается по типу от обычного указателя на функцию, даже если прототипы функции и метода внешне идентичны. Вспомним, как определяется указатель на функцию
C++
1
тип (*имя-указателя)(спецификация параметров);
Для сокращения записи применяют обычно оператор typedef. Объявление
C++
1
typedef тип (*тип-указателя)(спецификация параметров);
вводит новое имя типа. После этого указатель на функцию можно объявлять как
C++
1
тип-указателя имя-указателя;
Указателю присваивается адрес функции. В С++ разрешается присваивать адрес функции
либо явно задавая операцию получения адреса,
C++
1
имя-указателя = &имя-функции;
либо неявно
C++
1
имя-указателя = имя-функции;
В последнем случае имя функции неявно преобразуется в адрес.

Вызов функции через указатель выполняется опять же либо так:
C++
1
(*имя-указателя)(аргументы);
либо в более простой форме,
C++
1
имя-указателя(аргументы);
которая эквивалентна предыдущей
Рассмотрим несколько простых примеров. Допустим, у нас объявлен указатель на функцию
C++
1
int (*pf)(void);
Этому указателю можно присвоить адрес любой функции с таким же прототипом, в том числе и библиотечной. Например, такой прототип имеет функция-генератор случайного числа rand(), который прописан в заголовке библиотеки <cstdlib>.
C++
1
2
3
4
5
int f1(void) { return 1; } 
extern “C” int f2(void) { return 2; }
pf = f1;    cout << pf() << endl;               // вызов f1()
pf = f2;    cout << pf() << endl;               // вызов f2()
pf = &rand; cout << pf() << endl;               // вызов rand()
Как видите, спецификация компоновки никак не мешает косвенному вызову. Напишем теперь простой класс
C++
1
2
3
4
5
6
class Constant
{   int value;
   public: 
    Constant(const int &a): value(a) {}
    int get(void) { return value; }
};
Метод get() с виду имеет тот же прототип, что и функции в приведенном выше фрагменте. Однако попытки присвоить адрес метода get() указателю pf компилятор пресекает «на корню». Да и не совсем понятно, как этот адрес задавать. Вариантов два:
 объявить объект и взять адрес метода в объекте;
 не объявлять объект, а приписать префикс класса.
C++
1
2
3
4
// первый вариант
Constant A(5); pf = &A.get();               
// второй вариант
pf = &Constant::get;
Первый вариант вообще неверный: и Visual C++.NET 2003, и Borland C++ Builder 6 сообщают, что операцию взятия адреса & так использовать нельзя. Второй вариант – ближе к истине: оба компилятора сообщают только о невозможности выполнить преобразование типов. Попытки прописать преобразование явно — не проходят.
C++
1
2
3
pf = static_cast<int (*)(void)>(&Constant::get);
pf = reinterpret_cast<int (*)(void)>(&Constant::get); 
pf = (int (*)(void))(&Constant::get);
Все эти варианты вызывают ту же ошибку компиляции — невозможность преобразования типов. Таким образом, тип указателя на метод класса кардинально отличается от типа указателя на функцию: адрес метода нельзя присвоить указателю на функцию, даже если внешне их прототипы совпадают. Это становится понятным, если мы вспомним, что нестатические методы получают дополнительный параметр — указатель this.

Аналогично, нельзя присвоить обычному указателю на функцию адрес виртуального метода — в этом случае дело усугубляется еще наличием в составе объекта указателя на таблицу виртуальных методов. А вот со статическими методами картина другая! Статический метод не получает никаких «лишних параметров», поэтому его адрес можно присваивать обычному указателю на функцию без всяких преобразований. Добавим в класс Constant статическое поле и статический метод с нужным нам прототипом:
C++
1
2
3
4
5
6
7
8
class Constant
{   int value;
    static int d;
public: 
    Constant(const int &a): value(a) {}
    int get(void) { return value; }
    static int getd(void) { return d; } // --статический метод
};
Тогда нашему указателю pf можно присвоить адрес статического метода вторым из приведенных выше способов, например:
C++
1
pf = Constant::getd;        // или pf = &Constant::getd;
Префикс, естественно, необходимо писать.
Указатель на метод объявляется по-другому [1-8.3.3] — нужно задать префикс-имя класса:
C++
1
int (Constant::*pm)(void);
Такому указателю можно присваивать адреса обычных и виртуальных методов вторым их показанных выше способов, например
C++
1
pm = &Constant::get;        // или pm = Constant::get;
Адрес статического метода, так же как и адрес обычной функции, такому указателю присвоить нельзя — возникает ошибка трансляции: компилятор сообщает о невозможности выполнить преобразование типов.
И косвенный вызов метода выполняется по-другому — с помощью операции
выбора члена класса .* или ->* [1-5.5].
Несмотря на то, что указатель на член класса является отдельной, независимой от класса переменной, вызов метода по указателю возможен только при наличии объекта, например
C++
1
2
Constant A(5);
cout << (A.*pm)() << endl;
Выражение(A.*pm)() означает следующее: для объекта A вызвать метод, чей адрес записан в указателе pm. Слева от операции .* — объект, справа — указатель на метод. Скобки вокруг выражения A.*pm писать обязательно, так как приоритет операции () вызова функции выше, чем приоритет операции выбора члена класса .*.

В выражении (объект.*указатель) можно заменить часть «объект.» на «указатель->», например
C++
1
2
Constant *pc = new Constant(7);
cout << (pc->*pm)() << endl;
Обратите внимание: в выражении (pc->*pm) слева — обычный указатель на динамический объект, а справа — указатель на метод этого объекта. Это совершенно два разных типа указателя.
Интересно, что в системе Visual C++.NET 2003 размер sizeof(pf) указателя на функцию совпадает с размером sizeof(pm) указателя на член класса и равен 4 байта. А вот система Borland C++ Builder 6 выдает совершенно разные цифры: размер указателя на функцию равен 4 байтам, а размер указателя на метод — 12 байт!
11
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.