Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МВ_Прог_1курс_2n1часть.doc
Скачиваний:
11
Добавлен:
13.04.2015
Размер:
735.74 Кб
Скачать
    1. Механизм работы виртуальных функций

Виртуальные функции реализуются с использованием позднего связывания, которое заключается в следующем. Для каждого класса, имеющего виртуальные функции, транслятор строит таблицу виртуальных методов (vtable), которая хранит адреса виртуальных функций. Для любого класса в иерархии наследования адрес некоторой виртуальной функции имеет одно и то же смещение в vtable.

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

Вызов виртуальной функции происходит через vtable, на которую указывает vptr объекта, делающего вызов. Адрес вызываемой функции определяется не во время трансляции (это было бы раннее связывание), а во время выполнения программы (позднее связывание). Это и позволяет из функции set_year ( ), унаследованной от базового класса, обратиться к функции print( ), определенной в производном классе.

    1. Полиморфизм

На объект класса Birthdayможет указывать как переменная типаBirthday*, так и переменная типаDate*. Независимо от типа указателя виртуальные методы будут вызываться в соответствии с истинным классом объекта.

Пример.

Date *pd;

pd = new date(10, 10, 2000);

pd->print(); // Выводится объект класса date

delete pd;

pd = new birthday(10, 10, 2000, “Peter”);

pd->print(); // Выводится объект класса birthday

Тот факт, что один и тот же код выполняется по-разному ( pd->print() в данном примере и вызов виртуальной функции print() из функцииset_year() в предыдущем), произвел глубокое впечатление на программистов и был назван ими полиморфизмом. Выбор вызываемой функции зависит от того, на какую таблицу виртуальных методов указывает vptr объекта, а это определяется тем, конструктор какого класса инициализировал объект.

Замечание. Пример другого полиморфизма в С++ дает перегрузка функций.

    1. Множественное наследование

В С++ допускается множественное наследование, когда класс является производным от нескольких базовых классов.

class AB: public A, public B {...};

Класс AB наследует все компоненты классов A и B. Если компоненты базовых классов имеют одинаковые имена, неоднозначность устраняется операцией разрешения видимости:

A::имя или B::имя.

При множественном наследовании конструкторы базовых классов выполняются в том же порядке, в каком базовые классы перечисляются в объявлении производного класса. Деструкторы выполняются в обратном порядке.

Возможен случай, когда оба класса A и B имеют в числе предков класс R (рисунок а).

R R R

| | / \

A B A B

\ / \ /

AB AB

а) б)

В этом случае класс AB косвенно наследует два разных экземпляра класса R и при обращении к компонентам R из функций AB потребуется уточнить, какой R имеется в виду:

A::имя или B::имя.

Чтобы класс AB наследовал не два, а один экземпляр класса R, его надо объявить виртуальным при определении классов A и B.

class A: virtual public R {...

class B: virtual public R {...

Тогда возникнет ситуация, изображенная на рис.1-б, уточнение не понадобится и к тому же сэкономятся ресурсы.

Пример. Множественное наследование с общим предком.

    1. class C0 {

      public:

      void f() { cout << "f from C0" << endl; };

      };

      class C1: public C0 {

      public:

      void f() { cout << "f from C1" << endl; };

      };

      class C2: public C0 {

      public:

      void f() { cout << "f from C2" << endl; };

      };

      class C3: public C1, public C2 {

      public:

      void f() { cout << "f from C3" << endl; };

      };

      ///////////////////////// использование ///////////////

      C3 c;

      c.C1::f(); // uses vtbl for C1

      c.C2::f(); // uses vtbl for C2

      // ((C1)c).f(); - ОШИБКА!

      ((C0)(C1)c).f();

      Списки инициализации

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

Пример.Конструктор со списком инициализации.

// Базовый класс

class Complex {

public:

Complex( float re, float im )

{real = re; imag = im;};

}

// Производныйкласс

class Triplex : public Complex {

public:

Triplex(float re, float im, int co):

Complex(re, im) { color = co;};

}

Элементы-данные класса обычно инициализируются в теле конструктора, но и их можно поместить в список инициализации.

Triplex(float re, float im, int co):Complex(re, im), color(co) {};

Список инициализации является единственным средством инициализации элементов-констант, элементов-ссылок и элементов-объектов, конструкторы которых имеют параметры.

Примеры наследования

#include <iostream>

using namespace std;

class base {

int i, j ;

public:

void set(int a, int b) { i=a; j=b; }

void show() { cout << i << " " << j << "\n"; }

};

class derived : public base {

int k;

public:

derived(int x) { k=x; }

void showk() { cout << k << "\n"; }

};

int main()

{

derived ob(3);

ob.set(1, 2); // Обращение к члену класса base

ob.show(); // Обращение к члену класса base вывод 1 2

ob.showk(); // Обращение к члену класса derived вывод 3

cin.get();

return 0;

}

//////////

#include <iostream>

using namespace std;

class base {

int i, j ;

public:

void set(int a, int b) { i=a; j=b; }

void show() { cout << i << " " << j << "\n"; }

int y;

};

class derived : private base {

int k;

public:

derived(int x) { k=x; }

void showk() { cout << k << "\n"; }

base::show;//открываем доступ

};

int main()

{

derived ob(3);

// ob.set(1, 2); // Обращение к члену класса base

ob.show(); // Обращение к члену класса base вывод 1 2

ob.showk(); // Обращение к члену класса derived вывлд 3

cin.get();

return 0;

}

#include <iostream>

using namespace std;

class base {

int i;

public:

base() { cout<<"create base\n";}

~base() { cout<<"delete base\n";}

};

class derived : public base {

public:

derived() { cout<<"create derived\n"; }

~derived() { cout<<"delete derived\n"; }

};

int main()

{

{

derived ob;

derived *pob;

pob=new derived;

delete pob;

}

cin.get();

return 0;

}

//////////////////////////

#include <iostream>

using namespace std;

class base1 {

public:

base1() { cout<<"create base1\n";}

~base1() { cout<<"delete base1\n";}

};

class base2 {

public:

base2() { cout<<"create base2\n";}

~base2() { cout<<"delete base2\n";}

};

class derived : public base1, public base2{

public:

derived() { cout<<"create derived\n"; }

~derived() { cout<<"delete derived\n"; }

};

int main()

{

{

derived ob;

}

cin.get();

return 0;

}

Результат:

create base1

create base2

create derived

delete derived

delete base2

delete base1

///////////////////

#include <iostream>

using namespace std;

class base {

int i ;

public:

base(){i=5; cout<<"create "<<i<<" base \n";}

~base(){cout<<"delete "<<i<<" base \n";}

};

class derived : private base {

int k;

public:

derived(){k=6; cout<<"create "<<k<<" derived \n";}

~derived(){cout<<"delete "<<k<<" derived \n";}

};

int main()

{

{

derived ob;

}

cin.get();

return 0;

}

Результат:

create 5 base

create 6 derived

delete 6 derived

delete 5 base

#include <iostream>

using namespace std;

class base {

int i ;

public:

base(int x){i=x; cout<<"create "<<i<<" base \n";}

~base(){cout<<"delete "<<i<<" base \n";}

};

class derived : private base {

int k;

public:

derived(int x):base(x)

{k=x; cout<<"create "<<k<<" derived \n";}

~derived(){cout<<"delete "<<k<<" derived \n";}

};

int main()

{

{

derived ob(1);

derived *pob;

pob=new derived(2);

}

cin.get();

return 0;

}

Результат:

create 1 base

create 1 derived

create 2 base

create 2 derived

delete 1 derived

delete 1 base

#include <iostream>

using namespace std;

class base1 {

int i ;

public:

base1(int x){i=x; cout<<"create "<<i<<" base1 \n";}

~base1(){cout<<"delete "<<i<<" base1 \n";}

};

class base2 {

int i ;

public:

base2(int x){i=x; cout<<"create "<<i<<" base2 \n";}

~base2(){cout<<"delete "<<i<<" base2 \n";}

};

class derived : public base1,public base2 {

int k;

public:

derived(int x, int y):base1(x),base2(y)

{k=x+y; cout<<"create "<<k<<" derived \n";}

~derived(){cout<<"delete "<<k<<" derived \n";}

};

int main()

{

{

derived ob(1,2);

derived *pob;

pob=new derived(3,4);

delete pob;

}

cin.get();

return 0;

}

Результат

create 1 base1

create 2 base2

create 3 derived

create 3 base1

create 4 base2

create 7 derived

delete 7 derived

delete 4 base2

delete 3 base1

delete 3 derived

delete 2 base2

delete 1 base1

Второй пример главной функции:

int main()

{

{

base1 *pob;

pob=new derived(3,4);

delete pob;

}

cin.get();

return 0;

}

Результат:

create 3 base1

create 4 base2

create 7 derived

delete 3 base1

Третийпример главной функции:

int main()

{

{

base1 *pob;

pob=new derived(3,4);

delete (derived*)pob;

}

cin.get();

return 0;

}

create 3 base1

create 4 base2

create 7 derived

delete 7 derived

delete 4 base2

delete 3 base1

/////////////Пример разницы виртуального метода и не виртуального

#include <iostream>

using namespace std;

class base{

int i ;

public:

base(int x){i=x; cout<<"create "<<i<<" base \n";}

~base(){cout<<"delete "<<i<<" base \n";}

void showdata(){cout<<" "<<i<<" base \n";}

};

class derived : public base{

int k;

public:

derived(int x):base(x)

{k=x+x; cout<<"create "<<k<<" derived \n";}

~derived(){cout<<"delete "<<k<<" derived \n";}

void showdata(){cout<<" "<<k<<" derived \n";}

};

int main()

{

{

base *pob;

pob=new derived(3);

pob->showdata();

delete (base*)pob;

}

cin.get();

return 0;

}

Результат:

create 3 base

create6derived

3 base//т.е. вызвался метод по топу указателя – т.е. раннее связывание

delete3base

/////////////Пример тотже только с изменением витуальности функции

#include <iostream>

using namespace std;

class base{

int i ;

public:

base(int x){i=x; cout<<"create "<<i<<" base \n";}

virtual~base(){cout<<"delete "<<i<<" base \n";}

virtual void showdata(){cout<<" "<<i<<" base \n";}

};

class derived : public base{

int k;

public:

derived(int x):base(x)

{k=x+x; cout<<"create "<<k<<" derived \n";}

virtual~derived(){cout<<"delete "<<k<<" derived \n";}

void showdata(){cout<<" "<<k<<" derived \n";}

};

int main()

{

{

base *pob;

pob=new derived(3);

pob->showdata();

delete pob;

}

cin.get();

return 0;

}

Результат: