Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ТООП конспект весь.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
21.89 Mб
Скачать

Int main()

{

Derived d;

d.fun2();

Base *pb;

pb=&d;

??? pb -> fun2(); // в С++ будет вызываться Base : fun2

В java переопределение в аналогичной ситуации будет вызываться Derived fun2.

В C# будет вести себя, как С++.

В С++ и С# можно сделать и переопределение, используя зарезервированное слово virtual.

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

В java такого нет.

В С++ можно это обойти:

d.Base:: fun1();

В java и C# можно запретить вообще переопределять метод базового класса в производных классах.

В java используется зарезервированное слово find.

class B

{

public void f1() {}

public final void f1() {}

}

class D extends B

{

(x) public void f2 () {} //запрещено

}

Если в java весь класс объявлен как final, то нельзя для него создавать классы наследников.

В С# можно добиться аналогичного, используя зарезервированное слово sealed.

Это использование полиморфного поведения.

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

В литературе по С++ используется термин классический полиморфизм; реализуется с помощью виртуальных функций.

shape* figure [] ={

new circle (100,100,30),

new rectangle (80,120,80,10)

};

int size=5;

for (int i=0; i<size; ++i)

figure [i] -> show();

В С++ 5 раз будет вызван метод show базового класса shape. То есть вызываемый метод show вызывается не типом объекта, а типом указателя.

В java будут вызываться методы show конкретных классов. В java метод show определяется по типу объекта, а не по типу ссылки.

Поведение в java называют полиморфным, а в С++ не полиморфным.

Разница между сокрытием и переопределением метода как раз и заключается в том не полиморфные классы или полиморфные.

Для полиморфных классов реализуется переопределение. Для не полиморфных – сокрытие.

В java классы наследники (родственные) полиморфны по умолчанию. А в С++ и С# по умолчанию они не полиморфны.

Чтобы в С++ и С# обеспечить полиморфное поведение необходимо метод базового класса объявить виртуальной функцией.

class shape

{

protected:

int x,y;

public:

virtual void show() {}

};

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

class circle : public shape

{

int r;

public:

circle ( int x, int y, int r);

virtual void show () {}

};

В С# недостаточно объявить виртуальным метод базового класса и если мы для переопределения метода в производном классе не зададим override он будет просто скрытым.

C#:

class circle : shape

{

private int r;

public circle…

public override /*new – не хотим переопределять метод; хотим создать метод с такой же сигнатурой, несвязанный с базовым классом*/

void show () {}

}

Класс shape нужно объявить абстрактным классом.

Механизм реализации виртуальных функций.

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

  1. Если в классе объявлена хотя бы одна виртуальная функция, тогда для класса создается таблица виртуальных функций.

Для классов наследников будут созданы свои виртуальные функции. vtable

  1. Для класса создается дополнительное скрытое поле, указатель на vtable – vptr.

  2. Во все конструкторы класса встраивается код, заносящий в vptr адрес таблицы vtable.

  3. Вызов виртуальной функции производится косвенно.

shape *sp;

circle c(100,100,50);

sp=&c;

sp -> show();

vtable содержит указатель на все виртуальные функции своего класса.

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

Абстрактный класс – класс, для которого запрещено создание экземпляров, но можно создать указатели на абстрактный класс.

В примере класс shape должен быть абстрактным.

shape *figure []={new circle (50,50,10)};

for (int i=0; i<3; ++i)

figure[i] -> show();

sПравая фигурная скобка 14 hape s; // запрещено для абстрактных классов

s.show(); //

В С++ класс будет абстрактным, если в нем объявлен хотя бы один абстрактный метод. В С++ абстрактные методы называются чистыми виртуальными функциями.

class shape

{

protected:

int x,y;

public:

virtual void show () = 0;

};

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

class circle : public shape

{

int r;

public:

virtual void show ()

{

cout<<”I’m circle”<<endL;

}

};

В java и C# класс становится абстрактным по объявлению. В этих языках есть зарезервированное слово abstract.

Java:

abstract class shape

{

protected

int x,y;

public

void show () {}

}

shape s; // ссылка. Объект не создается.

s = new shape (); // (x) запрещено (создание объекта)

Абстрактные методы в java и C# создаются с помощью зарезервированного слова abstract.

public:

abstract void show();

Если в классе объявлен абстрактный метод, тогда сам класс должен быть объявлен абстрактным.

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

Интерфейсы в Java и C#

С точки зрения C# интерфейс - чисто абстрактный класс, т.е. класс, в котором нет никакой реализации.

В интерфейсе можно объявлять только абстрактные методы, статические константные поля и вложенные интерфейсы.

Java

Interface Runnable

{

Void run(); //abstract public

}

Runnable ir; //ссылка на абстрактный класс, нельзя создавать экземпляры интерфейса

(ошибка) new Runnable();

Ссылку на интерфейс можно связать с объектом класса, реализующего этот интерфейс.

Class A implements Runnable

{

Private int a;

Public void run(){}

}

Class B implements Runnable

{

Public void run(){}

}

Класс, реализующий интерфейс, должен содержать реализации всех методов этого интерфейса.

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

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

Ir = new A();

Ir.run(); //вызов метода из А

Ir = new В();

Ir.run(); //вызов метода из В

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

Пример

Обобщенный алгоритм сортировки по возрастанию элементов массивов на основе интерфейса.

Interface Comparable{ //способность к сравнению

Int compareTo(Object a2);

}

(метод может возвращать, допустим, -1, 0 или 1. Вызов планируется как a1.compareTo(a2))

Class A implement Comparable{

Private int a;

Public A(int x){a = x;}

Public A(){this(0);} //обращение из конструктора к конструктору

Public int getA(){return a;}

Public int CompareTo(Object a2){

A ra2 = (A) a2;

If (a < ra2.a) return -1; //this.a

Else if (a>ra2.a) return 1;

Return 0;

}

}

Class Utils{

Public static void sort(Comparable [] arr){

If (arr[i].CompareTo(arr[I+1])==1)

{

Comparable tmp=arr[i];

A[i]=a[i+1];

A[i+1]=a[tmp];

}

}

Class Test{

Public static void main(string [] args){

A[] arr = {new A(), new A (-1), new A(-10)};

Utils.sort(arr);

String [] sarr = {“Hello”, “A”, “Word”}

Util.sort(sarr);

}

В java есть стандартный интерфейс Comparable с одним методом CompareTo, возвращающий int. Класс String тоже реализует Compareble.

Для интерфейсов в java и C# разрешено и единичное, и множественное наследование. В java слово extends в C#:

Java

Interfaсe Aable{

Void f();

Int g(int a);

}

Interfaсe Bable{

Char fun(double);

Void f();

}

Interfaсe Cable extends Aable, Bable{}

Class Cl implements Cable{

//implements Aable, Bable

Public void f(){} //одна реализация двух описаний

Public int g(int a) {return a+1;}

Public char (class a) {return ‘A’;}

}

Можно объявить интерфейс, не содержащий никаких описаний. Такой интерфейс называют маркером.

Пример

Стандарнтый java interface Serializable{}

Если класс Сериализуемый, значит все его объекты можно представить в виде последовательности (серии байт). Эту последовательность можно записать на внешнее устройство или послать по сети. А можно выполнить и обратный процесс – десериализацию. Маркеры имеют смысл, если среда знает, как их использовать.

В C# пример выше будет отличаться только : вместо extends.

Проблемы, связанные со множественным наследованием

В C++

C:

C c;

A=*pa;

B *pb;

Pa = &c;

Pb = &c;

C.A::=a=s;

C.A is f();

Такой проблемы не возникает, если А и В – интерфейсы, а не реализации методов.

Частично проблема реализации констант.

Если в А и В есть константы с одинаковым именем, надо прямо указывать имя, А или В.

В C# проблемы от методов с интерфейсами возвращаются из методов.

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

Случаи:

По умолчанию в С++ реализуется 1-я схема.

2-я схема требует усложнений в исполняющей системе C++. Она реализуется с помощью виртуальных базовых классов.

C.c

Class A{

Public int g;

}

Class B:virtual public A{};

Class C:virtual public A{};

Class D:public B, public C{};

Вложенные классы

(класс в классе)

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

Внешний и вложенный классы является независимыми, т.е. методы вложенного класса не получают указатели на объекты внешнего. Но если методы вложенного дать объект внешнего, тогда этот метод сможет добраться до любой части объекта (открытой, закрытой и т.д.)

Внешний класс к внутреннему доступа не получает.

Class List{

Char* data;

Class ListNode{}; //вложенный класс

};

Вложенный класс может быть открытым, закрытым или защищенным. Если вложенный класс закрытый, то его могут использовать только методы внешнего класса. Если вложенный класс открытый, то его объекты можно создавать вне внешнего класса даже если нет ни одного объекта внешнего класса.

Class Outer{

Int a;

Public:

Class Inner{

Int I;

Public test (Outer o){

o.a=-I;

}

}

Void f(){}

};