Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

VPS в России, Европе и США

Бесплатная поддержка и администрирование

Оплата российскими и международными картами

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Указатели на объекты

Рассмотрим простой пример.

#include <iostream.h>
class A
{
};
class AB: public A
{
};
class AC: public A
{
};
void main ()
{
A *pObj;
A MyA;
pObj = &MyA;
cout << "OK A" << endl;
AB MyAB;
AC MyAC;
pObj = &MyAB;
cout << "OK AB" << endl;
pObj = &MyAC;
cout << "OK AC" << endl;
}

Это очень простой пример. Пустые классы, простое наследование… Единственно, что важно в объявлении этих классов - спецификаторы доступа в описании баз производных классов. Базовый класс (его будущие члены) должен быть абсолютно доступен в производном классе. Первый оператор функции main() - объявление указателя на объект класса A. Затем следует определение объекта-представителя класса A, следом - настройка указателя на этот объект. Естественно, при этом используется операция взятия адреса. Всё это давно известно и очень просто. Следующие две строки являются определениями пары объектов, которые являются представителями двух разных производных классов…

За объявлениями объектов в программе располагаются строки, которые позволяют настроить указатель на базовый класс на объект производного класса. Для настройки указателя на объект производного класса нам не потребовалось никаких дополнительных преобразований. Здесь важно только одно обстоятельство. Между классами должно существовать отношение наследования. Таким образом, проявляется важное свойство объектно-ориентированного программирования: УКАЗАТЕЛЬ НА БАЗОВЫЙ КЛАСС МОЖЕТ ССЫЛАТЬСЯ НА ОБЪЕКТЫ - ПРОИЗВОДНЫХ КЛАССОВ. Подобное, на первый взгляд, странное обстоятельство имеет своё объяснение.

Рассмотрим схемы объектов MyA, MyAB, MyAC:

MyA::=
A

MyAB::=
A
AB

MyAC::=
A
AC

Все три объекта имеют общий элемент (объекты производных классов - фрагмент) - представитель базового класса A. Исключительно благодаря этому общему элементу указатель на объект класса A можно настроить на объекты производных классов. Указателю просто присваивается адрес базового фрагмента объекта производного типа. В этом и состоит секрет подобной настройки. Как мы увидим, для указателя pObj, настроенного на объект производного класса, вообще не существует фрагмента объекта, представленного производным классом.

pObj

A
AC

Ниже пунктирной линии - пустота. Для того чтобы убедиться в этом, мы усложним структуру класса A, определив в нём функцию Fun1. Конечно же, эта функция ничего не будет делать. Но у неё будет спецификация возвращаемого значения и непустой список параметров. Нам от неё большего и не требуется. Лишь бы сообщала о собственном вызове…

class A
{
 public:
 int Fun1(int);
};
int A::Fun1(int key)
{
 cout << " Fun1( " << key << " ) from A " << endl;
 return 0;
}

Аналогичной модификации подвергнем производные классы AB и AC (здесь предполагаются вызовы функций-членов непосредственно из функции main(), а потому надо помнить о спецификаторе public), а затем продолжим опыты.

class AB: public A
{
public:
int Fun1(int key);
};
int AB::Fun1(int key)
{
 cout << " Fun1( " << key << " ) from AB " << endl;
 return 0;
}
class AC: public A
{
public:
int Fun1(int key);
int Fun2(int key);// В этом классе мы объявим вторую функцию.
};
int AC::Fun1(int key)
{
 cout << " Fun1( " << key << " ) from AC " << endl;
 return 0;
}
int AC::Fun2(int key)
{
 cout << " Fun2( " << key << " ) from AC " << endl;
 return 0;
}

Теперь мы займёмся функцией main(). Первая пара операторов последовательно из объекта запускает функцию-член производного класса, а затем - подобную функцию базового класса. С этой целью используется квалифицированное имя функции-члена.

MyAC.Fun2(2);    //Вызвана AC::Fun2()…
MyAC.Fun1(2);    //Вызвана AC::Fun1()…
MyAC.A::Fun1(2); //Вызвана A::Fun1()…

Следующие строки посвящены попытке вызова функций-членов по указателю на объект базового типа. Предполагается, что в данный момент он настроен на объект MyAC.

pObj->Fun1(2); //Вызвана A::Fun1()…

И это всё, что можно способен указатель на объект базового типа, если его настроить на объект производного типа. Ничего нового. Тип указателя на объект - базовый класс. В базовом классе существует единственная функция-член, она известна транслятору, а про структуру производного класса в базовом классе никто ничего не знает. Так что следующие операторы представляют пример того, что не следует делать с указателем на объекты базового класса, даже настроенного на объект производного класса.

//pObj->Fun2(2);
//pObj->AC::Fun1(2);

То ли дело указатель на объект производного типа! И опять здесь нет ничего нового и неожиданного. С "нижнего этажа бункера" видны все "этажи"!

AC* pObjAC = &MyAC;
pObjAC->Fun1(2);
pObjAC->Fun2(2);
pObjAC->AC::Fun1(2);
pObjAC->Fun1(2);
pObjAC->A::Fun1(2);

И, разумеется, указатель на объект производного класса не настраивается на объект базового.

//pObjAC = &MyA;

Основной итог этого раздела заключается в следующем: указатель на объект базового класса можно настроить на объект производного типа. Через этот указатель можно "увидеть" лишь фрагмент объекта производного класса - его "базовую" часть - то, что объект получает в наследство от своих предков. Решение о том, какая функция должна быть вызвана, принимается транслятором. В момент выполнения программы всё уже давно решено. Какая функция будет вызвана из объекта производного типа - зависит от типа указателя, настроенного на данный объект. В этом случае мы наблюдаем классический пример статического связывания.

Назад | Содержание | Вперед

 

Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...