|
Спецификаторы доступа базовых классовКогда класс Second порождается от First со спецификатором прав доступа private, например: class First {…}; class Second:First {…}; или class First {…}; class Second: private First {…}; то все наследуемые (т. е. защищенные и общедоступные) имена базового класса становятся приватными в производном классе. Когда класс Second порождается от First со спецификатором прав доступа protected, например: class First {…}; class Second: protected First {…}; то все наследуемые (т. е. защищенные и общедоступные) имена базового класса становятся защищенными в производном классе. Когда класс Second порождается от First со спецификатором прав доступа public, например: class First {…}; class Second: public First {…}; то все общедоступные имена базового класса будут общедоступными, а все защищенные будут защищенными в производном классе.
Порядок вызова конструкторов При создании экземпляра класса вызывается его конструктор. Если класс является производным, то должен быть вызван конструктор базового класса. Порядок вызова в С++ фиксирован. Если базовый класс, в свою очередь, является производным, то процесс рекурсивно повторяется до тех пор, пока не будет достигнут корневой класс. Например, class First {…}; class Second: public First {…}; class Third: public Second {…}; При создании экземпляра класса Third конструкторы вызываются в следующем порядке: First::First() Second::Second() Third::Third()
Порядок вызова деструкторов Деструкторы для производных классов вызываются в порядке обратном вызову конструкторов. Таким образом, порядок вызовов деструкторов, сгенерированных для разрушения экземпляра класса Third, будет следующим: Third::~Third() Second::~Second() First::~First()
Разрешение области видимости Порождая один класс от другого, можно прийти к такой ситуации, когда в нескольких классах используются переменные и функции с одинаковыми именами. Например: class A {public: int fun() {return 1;} }; class B: public A {public: int fun() {return 2;} }; void main() { A a; B b; int i = a.fun(); //i=1 int j=b.fun(); //j=2 } В этом случае компилятор действует по следующему алгоритму: если имя в базовом классе переобъявляется в производном, то имя в производном классе подавляет соответствующее имя в базовом. В С++ можно заставить компилятор «видеть» за пределами текущей области видимости. Для этого используются оператор разрешения видимости. Общая форма этого оператора такова: <имя класса>::<идентификатор из класса>, где <имя класса> - это имя базового или производного класса, а <идентификатор из класса> - это имя любой переменной или функции, объявленной в классе. Модифицируем наш класс B следующим образом: class B: public A {public: int fun() {return 2;} int fun1() {return A::fun();} }; Теперь вызов функции B.fin1() приведет к вызову функции fun() класса A. Динамическое связывание в ООП (полиморфизм) Полиморфизм – это свойство ООП, при котором одно и тоже сообщение может вызывать различные действия на этапе выполнения. Полиморфизм – это характеристика функций-членов, а не объектов. Несмотря на то, что полиморфизм реализуется через архитектуру класса, полиморфными могут быть только функции-члены класса, а не весь класс. В С++ полиморфная функция привязывается к одной из возможных функций только тогда, когда ей передается конкретный объект, т. е. в С++ вызов функции в исходном коде только обозначается, без точного указания на то, какая именно функция вызывается. Этот процесс называется позднее связывание. Процесс, при котором компилятор (как в традиционных языках), базируясь на исходном коде, вызывает фиксированные идентификаторы функций, а компоновщик заменяет эти идентификаторы физическими адресами, называется раним связыванием. То есть идентификаторы функций ассоциируются с физическими адресами до этапа выполнения, еще на стадии компиляции и компоновки. При раннем связывании программы выполняются быстрее, но существенно ограничены возможности разработчика. При позднем связывании остро встает вопрос об эффективности исполняемой программы. Способность объектно-ориентированных языков автоматически определять тип объекта на этапе выполнения программы называется RTTI (run-time type identification – идентификация во время выполнения). Виртуальные функции В С++ позднее связывание для функции определяется при ее объявлении с помощью ключевого слова virtual. Позднее связывание имеет смысл только для объектов, являющихся частью иерархии классов. Объявление функции виртуальной для класса (не используемого в качестве базового) синтаксически корректно, но приведет только к потере времени в момент выполнения. Виртуальные функции – функции, вызов которых зависит от типа объектов. С помощью виртуальных функций объект определяет свои действия. Правило: указатель на базовый класс может ссылаться на объект этого класса или любого другого, производного от базового. A* aobject; A B* bobject; B C* cobject; C cobject=&аobject // так нельзя делать aobject=&cobject. Пример: //f1.h #include <iostream.h> class Animal {public: /* virtual */ char* speak() {return "";} }; class Dog: public Animal {public: char * speak() {return "Gav!!!";} }; //f1.cpp #include "f1.h" void sound (Animal& i) {cout<<i.speak()<<endl;} void main () { Dog Sharic; sound(Sharic); //”” (на экран будет выведена пустая строка) } Решение проблемы – позднее связывание. Функцию speak() класса Animal достаточно объявить виртуальной, после чего компилятор запустит механизмы позднего связывания. //f1.h #include <iostream.h> #include <string.h> class Animal { protected: char *pname; public: Animal(char *AnName) { pname= new char [strlen(AnName)+1]; strcpy(pname, AnName); } virtual char * speak() { return "";} virtual char *name() { return pname;} };
class Dog: public Animal { public: Dog(char *name):Animal(name) {} char * speak() { char * phrase; phrase=strdup(pname); //дублирует строку, при этом вызывая функцию malloc() return strcat(phrase," Say Gav! "); } virtual char *sit() { char *phrase; phrase=strdup(pname); return strcat(phrase," sits"); } }; //f1.cpp #include "f1.h" void main () { Animal* p[2]={ new Animal("a"), new Dog("Sharic")}; cout<<p[0]->speak()<<endl; //выведет на экран пустую строку cout<<p[1]->speak()<<endl; //выведет на экран строку “Sharic Say Gav!” //cout<<p[1]->sit()<<endl; //ошибка компиляции: 'sit': is not a member of 'Animal' cout<<((Dog*)p[1])->sit()<<endl; } ![]() ![]() ЧТО ПРОИСХОДИТ ВО ВЗРОСЛОЙ ЖИЗНИ? Если вы все еще «неправильно» связаны с матерью, вы избегаете отделения и независимого взрослого существования... ![]() ЧТО ПРОИСХОДИТ, КОГДА МЫ ССОРИМСЯ Не понимая различий, существующих между мужчинами и женщинами, очень легко довести дело до ссоры... ![]() Живите по правилу: МАЛО ЛИ ЧТО НА СВЕТЕ СУЩЕСТВУЕТ? Я неслучайно подчеркиваю, что место в голове ограничено, а информации вокруг много, и что ваше право... ![]() ЧТО И КАК ПИСАЛИ О МОДЕ В ЖУРНАЛАХ НАЧАЛА XX ВЕКА Первый номер журнала «Аполлон» за 1909 г. начинался, по сути, с программного заявления редакции журнала... Не нашли то, что искали? Воспользуйтесь поиском гугл на сайте:
|