Сдам Сам

ПОЛЕЗНОЕ


КАТЕГОРИИ







Спецификаторы доступа базовых классов





Когда класс 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 г. начинался, по сути, с программного заявления редакции журнала...





Не нашли то, что искали? Воспользуйтесь поиском гугл на сайте:


©2015- 2025 zdamsam.ru Размещенные материалы защищены законодательством РФ.