- •Int main()
- •Int main()
- •Int main()
- •Int main()
- •Int main()
- •Int main()
- •Int main()
- •Int main ()
- •Int main()
- •Int main()
- •Int main()
- •Int main(){
- •Int main(){
- •Int main(){
- •Int main(){
- •Int main(){
- •Int main(){
- •Int main(){
- •Int main(){
- •Int main(){
- •Int main(){
- •Int main(){
- •Int main(){
- •Int main(){
Int main(){
Outer::Inner in;
Outer our;
In.test(out);
…
}
Вложенный класс может быть также объявлен в методе внешнего класса. Тогда он доступен только в этом методе. Для таких классов модификатор доступа не имеет смысла и не может быть указан. Такие классы называют внутренними (не вложенными, а именно так).
Class A{
Int a;
Public:
A (int x = 0):a(x){}
Void f(){
Class B{
Public:
Void test(A &x){x.a=-1;};
B b;
b.test(*this);
}
…
}
Int main(){
A x(100);
x.f();
…
}
Если без &, то x.a ==100, потому, что внутренний метод работает с копией. Если вставить &, то будет меняться.
Это поведение вложеннх классов характерно для С++ и С#.
Вложенные классы в java.
Язык java позволяет задавать статические и не статические вложенные классы. Java позволяет создавать безымянные вложенные классы.
Java
Статические вложенные классы ведут себя как вложенные классы в С++
Class A{
Private int a;
Public A(int x) {a = x;}
Public A(){this (0);}
Public static class B{
Public void test(A x){ A.a=-1;}
}
}
Class Test{
Public static void main(string [] args){
A r=new A();
A.B b=new A.B();
b.test(r);
}
}
В java все работает по ссылкам и само поле изменится.
Если вложенный класс не статический, то объекты этого класса нельзя создавать сами по себе. Можно только для какого-нибудь объекта внешнего класса.
Class A{
Private int a;
Public A(int x){a=x;}
Public A(){this (0);}
Public class B{public void test (){a=-1;}}
}
Методы нестатического сложенного класса В получают скрытый аргумент – ссылку на объект, включающий в себя как объект вложенного класса В, так и объект внешнего класса А, для которого был создан объект В.
-//- main{
A r = new A();
A.B b =r.newB();
}
Создание объекта. Дальше действия будут идти внутри контекста А.В.
Безымянные вложенные классы
Java
Class A{
Public void f1(){}
Public int f2(){return 2;}
Interface IA{public void f();}
}
Main:
A ra = new A(){
Public void f1(){}
};
Ra.f1(); //вызов этой реализации переопределенной функции
}
Такая конструкция говорит java создать новый безымянный класс как наследник А. Можно объявлять у него поля, методы и т.д. Далее создается объект этого безымянного класса и возвращается ссылка на него.
Для интерфейса:
IA ia = new IA(){ public void f(){}};
Будет создан новый безымянный класс, реализующий интерфейс. Надо реализовать все методы или класс будет абстрактным.
Interface II{}
New II(){
Public void fun(){}
f.fun();
}
Обработка исклбчений
Старые способы обработки исключений
Создать глобальную переменную и в этой переменной будет храниться код ошибки. Каждая функция, прежде чем начать работать, проверяет текущее значение глобальной переменной. Если кода ошибки нет, то функция выполняется. Если код ошибки выставлен, функция пытается обработать ошибку. Если удалось исправить, функция сбрасывает код и работает дальше. Если не может справиться с ошибкой, то передает управление в вызвавшую её функцию.
Недостатки схемы:
К глобальной переменной есть доступ из функций, не принадлежащих к нашей библиотеке. Случайно или намеренно могут исправить код ошибки и нарушить работу.
Если имена глобальных переменных в двух библиотеках совпали, тогда эти библиотеки нельзя использовать одновременно.
Если имена глобальных переменных разные, но коды ошибок совпадают, но они представляют разные ошибки.
Такая схема обработки исключений не обязательна. Не создает обязательства контролировать ошибки.
Схема не объектно-ориентирована.
Каждая функция возвращает код ошибки
Такой подход лишен 1-го и 2-го недостатков предыдущей схемы, но остальные недостатки присутствуют.
Set jump/long jump
Setjump – запоминает состояние процесса в точке её вызова.
Longjump – переводит процесс в состояние, запомненное прежде с помощью setjump.
Недостатки
Схема не объектно-ориентированная. Чтобы схема была обектно-ориентированной, она должна поддерживать раскрутку стека (разматывание стека, stack unwinding) для всех объектов вызываются деструкторы.
Стандартная обработка исключений в С++
Бывают стандартные и асинхронные исключения.
Если программа в принципе не может определить время возникновения исключения, такое исключение – асинхронное.
Если программа может определить, когда произойдет исключение, тогда исключение синхронное.
Стандартная схема обработки исключений в С++ ориентирована только на синхронные исключения.
Синхронные исключения чаще всего генерирует сама программа.
F() -> исключение -> abort() – библиотека прерывания работы при ошибке.
Блоки {}
Try – блоки нужны для отслеживания момента ошибки и контроля за ответом.
Catch – обработчик исключения должен быть для каждого try хотя бы один.
Throw – конструкция, позволяющая генерировать исключение.
В С++ типом исключения может быть любой допустимый тип.
Пример применения
Try{
F();
Cout<<”Ok”<<endl;
}
Catch(int){
Cout<<”Error!”<<endl;
}
Void f(){ throw -1;}
Если возникло исключение, то управление передается в подходящий catch без окончательного выполнения try.
Если исключение не возникло, то полностью выполняется try, catch не выполняется и управление передается далее.
Возможен вариант:
Catch (int a){}
В java иначе писать и нельзя.
Try{
F();
}
Catch (int){
Cout<<”Int”<<endl;
}
Catch(char *){
Cout<<”Char *”<<endl;
}
Если типы родственные или подобные, то порядок следования catch имеет значение, т.к. выполняется первое подходящее.
Class A{}
Class B:public A{}
Try{ f();}
Catch (A){}
Catch (B){}
Сначала должен идти обработчик производного типа, а потом базового, иначе работать всегда будет базовый.
То же самое касается указателей базового и производного классов.
Указатель void* может обрабатывать любые исключения,чьи типы – указатели.
C#
Catch (…){}
Перехватывает исключения любого типа, потому всегда должен быть самым последним.
Если вложенные блоки try и для исключения внутреннего try нет подходящего обработчика, то он ищется среди внешних.
Try{
Try{
Throw 1.73}
Catch(char *){}
}
Catch(double){}
Управление будет передано Catch(double){}
Для косвенного случая
Void f1(){
Try{
F2();
} catch(double){}
}
Void f2(){
Try{ throw 1.73;} catch (char *){}
}
Управление будет передано в catch(double){} функции f1.
Повторная генерация исключений для случаев, когда исключение обработано не до конца в первом обработчике.
Void f1(){
Try{f2();} catch (double x){x = 1.73}
}
Void f2(){
Try { throw 1.73;} catch (double){throw;}
}
Throw в catch в f2 перенаправляет обработку в catch в f1.
Спецификация исключений
С++
Void f(int) x throw(int){
If (!x) throw 0;
Cout<<”1/x=”<<1/x<<endl;
}
//прототип
Void f(int x) throw(int);
