Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

техпрог / Comp-Sci-10

.pdf
Скачиваний:
44
Добавлен:
10.02.2015
Размер:
914.81 Кб
Скачать

Материалы к лекции 10

Объектно-ориентированное программирование

Указатели на объект производного класса

Указатель базового класса может указывать на объект любого класса, производного от этого базового класса. Обратный порядок недействителен.

Пример.

#include <iostream> using namespace std; class base {

int x; public:

void setx(int i) { x = i; } int getx() { return x; }

};

class derived : public base { int y;

public:

void sety(int i) { y = i; } int gety() {return y; }

};

int main()

{

base *p; // указатель базового класса base b_ob; // объект базового класса derived d_ob; // объект производного класса

//использование указателя p для доступа к объекту базового

класса

p = &b_ob;

p->setx(10); // доступ к объекту базового класса

cout << "Объект базового класса x: " << p->getx() << '\n';

//использование указателя p для доступа к объекту производного класса

p = &d_ob; // указывает на объект производного класса p->setx(99); // доступ к объекту производного класса

//т.к. p нельзя использовать для установки y, делаем это напрямую

d_ob.sety(88);

cout << "Объект произв. класса x: " << p->getx() << ' '; cout << "Объект произв. класса y: " << d_ob.gety() << '\n'; return 0;

}

Полиморфизм. Виртуальные методы

В C++ метод производного класса может иметь иную реализацию («поведение»), чем метод (с тем же именем и сигнатурой) базового класса и

поведение метода будет зависеть от вызывающего объекта. Такое поведение называется полиморфным (“имеющим много форм”). Полиморфное наследование можно реализовать двумя способами:

1.Переопределением методов базового класса в производном классе

2.Использованием виртуальных методов

Пример.

Диаграмма классов в Rational Rose

Диаграмма классов в Visual Studio

// CPolymorph1.cpp : Виртуальные функции - пример1

//

#include "stdafx.h" #include <iostream> using namespace std;

class Shape

{

protected:

string Name; public:

//Конструкторы: Shape(){Name = "NoName";} Shape(string s){Name = s;}

//Операции:

virtual void Draw()

{

cout<<"\n -Shape-Draw- \n";

}

};

class Circle : public Shape

{

public: Circle():Shape("circle"){};

Circle(string name):Shape(name){}; void Draw()

{

cout<<"\n -Circle-Draw- \n";

}

};

class Oval: public Circle

{

public: Oval():Circle("Oval"){}; void Draw()

{

cout<<"\n - Oval-Draw- \n";

}

};

class Hexagon: public Shape

{

public: Hexagon(){Name="Hexagon!";} Hexagon(string s):Shape(s){}; void Draw()

{

cout<<"\n - Hexagon-Draw- \n";

}

};

int _tmain(int argc, _TCHAR* argv[])

{

Shape ob[4]={Circle("Krug"),Circle(),Oval(),Hexagon()}; for(int i = 0; i < 4; i++)

{

ob[i].Draw();

}

Shape *pob[4]={new Circle("Krug"), new Circle(),

new Oval(),

new Hexagon()}; for(int j = 0; j < 4; j++)

{

pob[j]->Draw();

}

return 0;

}

Рис. Сравните результат: метод ob[i].Draw() (объекты массива Shape ob) печатает -Shape-Draw- , несмотря на то, что объекты массива ob создавались различными конструкторами (Circle(),Oval(),Hexagon()). Метод pob[j]- >Draw() печатает что нужно: «понимает, какой объект его вызвал».

Интерпретация вызова функции в исходном коде при выполнении определенного его блока называется связыванием имени функции.

Связывание, происходящее во время компиляции, называется

статическим (или ранним) связыванием.

При динамическом (или позднем) связывании, компилятор генерирует код, позволяющий выбирать нужный виртуальный метод во время работы программы. Компиляторы при обработке виртуальных функций добавляют к каждому объекту скрытый элемент, который содержит указатель на массив адресов функций. Этот массив называется таблицей виртуальных методов (vtbl) и содержит адреса виртуальных функций, объявленных для объектов этого класса. Когда вызывается виртуальная функция, программа просматривает адрес таблицы, сохраненный в объекте, и обращается к таблице адресов функций.

Тот же пример, но без virtual:

// CPolymorph1.cpp : Виртуальные функции - пример1

//

#include "stdafx.h" #include <iostream> using namespace std;

class Shape

{

protected:

string petName; public:

// Конструкторы: Shape(){petName = "NoName";}

Shape(string s){petName = s;} // Операции:

//virtual void Draw()

void Draw()

{

cout<<"\n -Shape-Draw- \n";

}

};

. . . Тот же код, что и в предыдущем примере

int _tmain(int argc, _TCHAR* argv[])

{

Shape ob[4]={Circle("Krug"),Circle(),Oval(),Hexagon()}; for(int i = 0; i < 4; i++)

{

ob[i].Draw();

}

Shape *pob[4]={new Circle("Krug"),new Circle(),new Oval(),new Hexagon()}; for(int j = 0; j < 4; j++)

{

pob[j]->Draw();

}

return 0;

}

Пример.

Диаграмма классов в Rational Rose

Диаграмма классов в Visual Studio

// CPolymorph2.cpp :

//

#include "stdafx.h" #include <string> #include <iostream> using namespace std;

class Transport

{

protected:

string name; // Constructors:

public:

Transport() { name = "NoName"; } Transport(string s) { name = s; }

virtual void PlaySignal()

//void PlaySignal()

{

cout<<"\n No Signal!\n";

}

virtual void Info()

{

cout<<"\nTransport: "<<name <<"\n";

PlaySignal();

}

};

class Velo : public Transport

{

int wheels; // число колѐс public:

Velo(string name,int w): Transport(name){wheels=w;} void PlaySignal()

{

cout<<"\nDin - Din";

}

};

class Moto : public Transport

{

int cylinders; // число цилиндров public:

Moto(string name, int cy) : Transport(name) { cylinders = cy; } void PlaySignal()

{

cout<<"\nBap - Bap";

}

};

class Auto : public Transport

{

int doors; // число дверей public:

Auto(string name, int d) : Transport(name) { doors = d; } void PlaySignal()

{

cout<<"\nBee Bip";

}

};

int _tmain(int argc, _TCHAR* argv[])

{

Transport p[4]; int i;

p[0]= Velo("Sputnik",2); p[1]= Moto("Ural",2); p[2]= Auto("Lada",4); p[3]= Auto("Niva",2);

for(i = 0; i < 4; i++)

{

p[i].Info();

}

Transport* pp[4];

pp[0]= new

Velo("Sputnik",2);

pp[1]= new

Moto("Ural",2);

pp[2]= new

Auto("Lada",4);

pp[3]= new

Auto("Niva",2);

for(i = 0; i

< 4; i++)

{

pp[i]->Info();

}

return 0;

}

Несколько правил использования виртуальных методов

Если объявление метода класса в базовом классе начинается с ключевого слова virtual, то этот метод становится виртуальным для базового класса и всех производных классов.

Ключевое слово virtual указывается только в спецификации базового класса, не требуется повторять его в спецификации производного класса.

Если виртуальный метод вызывается с помощью ссылки на объект или указателя на объект (->), в программе реализуется динамическое связывание и будет использоваться метод, определенный для типа объекта, а не для типа указателя или ссылки.

Transport* ppp; // Transport* - тип указателя ppp= new Velo("Sputnik",2); // Velo – тип объекта ppp->PlaySignal(); // PlaySignal() из Velo

Конструкторы не могут быть виртуальными.

Деструкторы должны быть виртуальными. Когда базовый указатель ссылается на объект производного класса, вызывается деструктор базового

класса. Если управление памятью динамически распределяемой области памяти осуществляется в производном классе, она не будет возвращена базовым деструктором, в результате произойдет утечка памяти. Поэтому деструктор базового класса следует объявлять виртуальным.

Соседние файлы в папке техпрог