При першому знайомстві з покажчиками в C ++ (см. Вказівники на C++. Частина 1 ) може скластися спрощене уявлення, що покажчики можуть вказувати тільки на окремі змінні вбудованих (skalyarnıx) типів C ++, і що це просто ще одна, альтернативна форма доступу до таких змінних. У такому застосуванні покажчики були б приємним доповненням мови, але з дуже обмеженими можливостями.
При більш уважному вивченні покажчиків C ++ ми виявляємо, що покажчик може бути адресою розміщення (вказувати на) будь-якого допустимого об'єкта в програмі: структуры, об'єкта класу, массива, функции, або знову ж покажчика на деякий об'єкт, або покажчика на покажчик і так далі… Це робить покажчики мало не найпотужнішим інструментом програміста на C ++ ... але і найнебезпечнішим в сенсі можливих прихованих помилок.
Розглянемо такі варіанти докладніше. Найпростішим варіантом буде використання покажчиків на складові об'єкти (об'єкти класів і структури). Але вже таке використання покажчиків відкриває широкі перспективи в C ++, як ми зараз побачимо.
Итак, ми вже знаємо, що опис класу не створює ніяких нових об'єктів даних в програмі - це тільки опис деякого шаблону, нового типу даних, згідно з яким будуть створюватися реальні об'єкти данных.
При створенні (оголошенні) нових об'єктів даних ми можемо обчислювати адреса цих об'єктів і привласнювати їх вказівниками на об'єкти этого класса. Напишемо найпростішу програму, operiruyushtuyu з покажчиками на об'єкти деякого класу (файл ex1.cc):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <iostream> using namespace std; class my { // наш собственный класс private: int number; public: my( int numb ) : number( numb ) {}; void show( void ) { cout << "my number is " << number << endl; } }; int main( int argc, char **argv, char **envp ) { my m1( 10 ), m2( 15 ), m3 ( 25 ); // 3 объекта нового класса my *pm1 = &m1, *pm2 = &m2, *pm3 = &m3; // 3 указателя на эти же объекты m1.show(); m2.show(); m3.show(); pm1->show(); pm2->show(); pm3->show(); return 0; } |
Тут ми бачимо відносно нову в наших темах конструкцію:
1 | my( int numb ) : number( numb ) {}; |
Это конструктор нового класу мій, але с параметром створення. При виклику він викликає конструктор батьківського класу (number(знімілий)), передаючи йому це ж значення параметра. Наступні далі дужки {} обрамляють порожній блок коду, який означає, що нічого більш понад виклику батьківського конструктора робити не потрібно.
згадуємо, що вся послідовність конструкторів всіх батьківських класів – викликається (в порядку зворотному спадкоємства) при виклику конструктора породженого класу, але це тільки для конструкторів без параметрів. У разі параметризованих конструкторів родіелей вам прийдется викликати явно.
Але ми на цьому відволіклися в сторону від предмета нашого викладу… А тепер саме час компілювати і подивитися виконання отриманої нами програми:
Поки нічого принципово нового, і все це сильно схоже на те, як ми працювали б з покажчиками на змінні типу double, скажем.
Згадаймо, на додаток, що оператор новий для динамічного створення нового об'єкта:
а) викликає менеджер динамічного управління пам'яті і виділяє новий обсяг під розміщення такого об'єкта;
б) викликає конструктор відповідного класу (типу даних) для початковоїрозмітки (ініціалізації) виділеної пам'яті. Злегка модифікуємо свій приклад (файл ex2.cc):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #include <iostream> using namespace std; class my { private: int number; public: my( int numb ) : number( numb ) {}; void show( void ) { cout << "my number is " << number << endl; } }; int main( int argc, char **argv, char **envp ) { my m1( 10 ), m2( 15 ), m3 ( 25 ); my *pm1 = new my( 10 ), *pm2 = new my( 15 ), *pm3 = new my( 25 ); pm1->show(); pm2->show(); pm3->show(); delete pm1; delete pm2; delete pm3; return 0; } |
Збірка і виконання:
Поки ще не відбулося ніяких радикальних змін ..., але звернемо увагу на те, що в програмі (а це могла б бути як завгодно велика програма) взагалі не оголошені і не використовуються в явному вигляді об'єкти - Програма оперує тільки покажчиками на об'єкти.
И, нарешті, ми наближаємося до тієї техніки у використанні покажчиків на об'єкти, яка робить покажчики найпотужнішим інструментом роботи з класами і їх об'єктами в C ++. А именно: віртуалізація функцій-членів класу і поліморфізм.
концепція поліморфізму (мінливості форми, многоликости) – один з основних напрямків розвитку C ++ від його попередника мови C. Воно полягає в тому, що прабатько цілого сімейства успадкованих класів оголошує деякі свої функції-методи як віртуальний. А в різних успадкованих класах ці функції-методи перевизначаються по різному.
Это означает, що в розрізняються успадкованих класах (родинних) функція-метод з одним і тим же ім'ям буде виконуватися кілька розрізняються способом, в залежності від конкретного класу в якому вона використовується.
Продемонструємо цю техніку на прикладі. створимо (файл ex3.cc) класс, описує взагалі будь-які плоскі фігури на площині (це можуть бути елементи деякого 2D ігрового сценарію):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <iostream> #include <math.h> using namespace std; class figure { // абстрактный класс protected: double x, y; // центр: x, y double r; // масштаб фигуры public: figure( double r ) { x = y = 0.0; this->r = r; } // далее - 3 чистые (пустые) виртуальные функции: virtual const char *title( void ) = 0; virtual double area( void ) = 0; virtual double perimeter( void ) = 0; void show( void ) { cout << "фигура " << title() << ": площадь=" << area() << ", периметр=" << perimeter() << endl; }; }; |
Тут перед нами зразок того, що в C ++ називається абстрактний класс: класс, в якому визначена хоча б одна віртуальна функція з визначенням виду:
1 | virtual double area( void ) = 0; |
У показаному класі таких функцій аж 3. природно, що створити об'єкт абстрактного класу неможливо, в ньому є функції, тіло яких не визначено. Спроба оголошення об'єктів такого класу викличе синтаксичну помилку. Але від такого класу можна успадковувати, створювати похідні класи, які успадкують загальні властивості родового абстрактного (например, координати x і і центру фігури і її розмір R).
визначимо 3 похідних от фігура класса: коло, квадрат і рівносторонній трикутник:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class circle : public figure { // класс всех кругов public: circle( double r ) : figure( r ) {}; const char *title( void ) { return "круг"; }; virtual double area( void ) { return M_PI * r * r; }; virtual double perimeter( void ) { return 2. * M_PI * r; }; }; class square : public figure { // класс всех квадратов public: square( double r ) : figure( r ) {}; const char *title( void ) { return "квадрат"; }; virtual double area( void ) { return r * r; }; virtual double perimeter( void ) { return 4. * r; }; }; class triangle : public figure { // класс всех равносторонних треугольников public: triangle( double r ) : figure( r ) {}; const char *title( void ) { return "треугольник"; }; virtual double area( void ) { return sqrt( 3. ) * r * r / 4.; }; virtual double perimeter( void ) { return 3. * r; }; }; |
Тепер ми готові створити програму, для якої будувалися всі ці приготування: створити довільне число різних 2D геометричних объектов, над якими можемо виконувати однакові (віртуальні) дії, не дивлячись на їх різну природу:
1 2 3 4 5 6 7 | int main( int argc, char **argv, char **envp ) { figure *fgs[] = { new circle( 3 ), new square( 3 ), new triangle( 3 ) }; int n = sizeof( fgs ) / sizeof( fgs[ 0 ] ); for( int i = 0; i < n; i++ ) fgs[ i ]->show(); for( int i = 0; i < n; i++ ) delete fgs[ i ]; return 0; } |
На цьому простому прикладі показано то, що в об'єктної моделі мови C ++ називається поліморфізм. І ця властивість є одним з найпотужніших виразних інструментів мови C ++. І реалізується ця техніка завжди через указатели на объекты (фігурка *).
Ось як буде виглядати компіляція і виконання нашого прикладу (ex3.cc) в терміналі операційної системи Linux при використанні GCC компілятора з мови C ++ (це буде зайвий раз підтвердженням того, що програмування на мові C ++ в меншій мірі залежить від операційної системи):
Ще раз звернемося до коду показаного прикладу, і зайвий раз зафіксуємо те надзвичайно важлива обставина, що покажчики C ++ завжди типізовані: покажчик не може бути «покажчиком на щось». Мова C ++ – це мова зі суворої іменний tipizacieй. Типом же покажчика є: покажчик на тип вказується ним змінної, наприклад «покажчик на double». Покажчики на різні типи несумісні між собою по присвоєнню і порівняно.
Техніка віртуальних функцій і поліморфізму є настільки основними для всієї філософії C ++, що вимагають окремого докладного розгляду. Про це поговоримо в одному з наступних уроків.
По темі – покажчик вказує на початок області пам'яті (зберігає адресу). І нема чого далі писати про це, т.к. при використанні покажчика ти просто береш значення за цією адресою і приводиш його до потрібного типу (будь-якого).
Якщо і розкривати тему – то треба ІМХО написати про те, які проблеми вирішували покажчики (у цілому ООП, т.к. тут ця тема майже не розкривалася) і написати за мотивами покажчики vs посилання (тема вже розкрита у Маєрс, можна взяти там основу).
Але ти як то різко ти перейшла до поліморфізму. Це не погано, але користувачам, які читали тільки твій сайт тепер мало що зрозуміло. До цього, описуючи класи ти не писала про віртуальні функції і навіть про спадкування. А тут раптом на читача впало все це і абстрактні класи.
В кінці статті ти описала поліморфізм – я думаю це варто виділити в окрему статтю. Але перед цим написати статтю про “Спадкування класів в C ++” (це остання тема в твоєму плані на головній сторінці)
Автор статті – НЕ адмін сайту )) Простим йому це, так як він не перечитував всі наші статті
ось згоден. На початку про покажчики, а потім якогось фіга про поліморфізм. Якось занадто в купу все.
згоден. Останні статті з розділу класів складні.
Скажіть, а хто адмін? Лілія або Ігор? Я завжди думав, що від імені адміна пише Ігор.
А, що означають рядки? У першому лістингу
pm1->show();
pm2->show();
pm3->show();
об'єкт класу (типу) мій – m1.
Покажчик на цей об'єкт – pm1.
m1.show() – виклик методу (функции) шоу() класу my для об'єкта m1.
pm1->шоу() – виклик точно того ж методу show() класу my через указатель на об'єкт m1.
Взагалі-то m1 – це один об'єкт, а pm1 – це покажчик на зовсім інший об'єкт того ж класу (в динамічної пам'яті).
це на лістингу 2.
1p1rlh
Підкажіть як усунути цю помилку?
Ошибка (активно) E0020 ідентифікатор “M_PI” не вказано Project1