Страница 1 из 2
Про области видимости и классы-функции
Добавлено: 25 авг 2005, 13:29
HKarel
Как известно С++ не поддерживает вложенные ф-ции, однако, нередко возникают ситуации когда наличие последних было бы совсем не лишним. Например, когда ф-я имеет несколько точек выхода, и перед выходом нужно выполнить некоторую последовательность действий. К счастью существуют обходные пути, которые позволяют хотя бы частично решить эту проблему. Почему частично? Чтобы это понять рассмотрим код:
Код: Выделить всё
class A
{
private:
int f1();
public:
int f2();
int f3();
};
int A::f3()
{
// Описываем локальный класс-функцию
class TFunc
{
A *const a;
public:
TFunc( A *a_ ) : a( a_ ){}
int operator ()()
{
a->f2(); // Здесь все хорошо, можем
// обратиться к ф-ции f2
a->f1(); // А в этом месте возникают проблемы.
// Компилятор пишет, что ф-я f1 недоступна
// в области видимости класса TFunc.
return 0;
}
} func( this );
...
return func();
...
return func();
}
Собственно, вопрос заключается в том, как разрешить проблему видимости, при условии, что f1 должна оставаться private, либо protected ?
Добавлено: 25 авг 2005, 14:19
Kolinus
посмотри на друзей (friend)
если класс сделать врендом другого то насколько помню он может дергать защищенные функции
Добавлено: 25 авг 2005, 14:28
HKarel
Это первое, что пришло мне в голову, вопрос в том куда поместить директиву
Добавлено: 25 авг 2005, 14:42
WinMain
TFunc нужно описать не внутри метода, а внутри описания самого класса, а потом объявить его другом этого класса.
Код: Выделить всё
class A
{
private:
int f1();
public:
int f2();
int f3();
class TFunc
{
A *const a;
public:
TFunc( A *a_ ) : a( a_ ){}
int operator ()()
{
a->f2();
a->f1();
return 0;
}
};
friend TFunc;
};
int A::f3()
{
TFunc func( this );
//...
return func();
//...
return func();
}
Добавлено: 25 авг 2005, 15:17
HKarel
В принципе это решение, но оно имеет существенные минусы:
1. Загромождение хедер файла лишним описанием, тем более что TFunc используется только в одной ф-ции A::f3().
2. А если у меня появится еще ф-я f4, а потом и f5 (так и есть на самом деле) и каждая должна использовать свои TFunc, то загромождение хедера будет очень сильным + для каждой ф-ции придется придумывать свое имя, либо определять псевдовходящие параметры для того чтобы обеспечить перезагрузку, бр-р-р ...
В таком случае будет проше описать обычные ф-ции в общей области видимости, сделать их друзьями для класса А, что опять же не очень эффективно, т.к. использоваться они будут 2-3 раза и только для конкретных ф-ций класса А. В таком случае уж лучше goto использовать, хотя изначально от этого и хотелось уйти.
Добавлено: 25 авг 2005, 16:02
WinMain
1. Саму реализацию методов TFunc необязательно описывать в заголовочном файле, там лучше оставить только объявления его методов. А в файле .срр уже их реализовывать.
2. Вместо оператора
goto внутри процедуры можно применить одиночный цикл, а оператор
break выводит процедуру из тела цикла, например:
Код: Выделить всё
int MyProc()
{
// код по инициализации каких-то параметров...
// ....
do {
// основной код процедуры...
if (func1() == FALSE)
{
// обработка ошибки...
break;
}
if (func2() == FALSE)
{
// обработка ошибки...
break;
}
// ........
} while(0);
// ....
// код по завершению процедуры...
return 0;
}
Добавлено: 25 авг 2005, 16:41
HKarel
Ваш первый пункт натолкнул на некоторые сумасбродные мысли и даже заставил сесть за компилятор
Если бы можно было делать так, то было бы вполне приемлемо
Код: Выделить всё
class A
{
int f1(){ return 0; }
friend class TFunc;
class TFunc
{
A *const a;
public:
TFunc( A *a_ ) : a( a_ ){}
int operator()();
};
public:
int f2(){ return 0; }
int f3();
int f4();
};
//---------------------------------------------------------------------------
A::f3()
{
int TFunc::operator()()
{
// реализация специфичная для ф-ции f3
}
TFunc func( this );
}
//---------------------------------------------------------------------------
A::f4()
{
int TFunc::operator()()
{
// реализация специфичная для ф-ции f4
}
TFunc func( this );
}
Однако все оказалось гораздо прозаичнее, компилятор (борландовый) допускает только конструкции вида:
Код: Выделить всё
class A
{
int f1(){ return 0; }
friend class TFunc;
class TFunc
{
A *const a;
public:
TFunc( A *a_ ) : a( a_ ){}
int operator()();
};
public:
int f2(){ return 0; }
int f3();
int f4();
};
//---------------------------------------------------------------------------
int A::TFunc::operator()()
{
...
}
//---------------------------------------------------------------------------
A::f3()
{
TFunc func( this );
}
//---------------------------------------------------------------------------
A::f4()
{
TFunc func( this );
}
А это значит, что для каждой ф-ции класса А надо будет писать свою ф-цию TFunc - чего бы не хотелось. Кстати, ребятм с RSDN удалось скомпилить исходный пример на нескольких компиляторах, включая MS VC7.1, единственно открытым остался вопрос: "Насколько компиляция исходного примера соответствует стандарту ?".
По второму пункту: принципиально не хочу сейчас обсуждать правила хорошего проектирования процедур, но в жизни случается нак, что код процедуры может занимать не одну сотню строк. В такой ситуации границы конструкции do-while плохо просматриваемы, поэтому я предпочел бы все таки goto.
Добавлено: 25 авг 2005, 17:48
WinMain
Можно попробовать воспользоваться шаблонами.
В заголовке класса объявляешь некий общий шаблонный метод обработки ошибок, а его специализацию пишешь для каждого конкретного случая.
А параметром шаблона может быть код ошибки или числовой идентификатор вызывающего метода.
Т.е. вместо набора методов типа
func1(), func2(), func3(), ... объявляешь в заголовке класса
template <const int ID> func();
а потом теле файла .срр пишешь их специализацию:
Код: Выделить всё
// вместо чисел можно указать мнемонические константы
template<> A::func<1>()
{
// ...
}
template<> A::func<2>()
{
// ...
}
template<> A::func<3>()
{
// ...
}
А вызываться методы будут соответственно
func<1>(), func<2>(), ...
Добавлено: 25 авг 2005, 18:14
HKarel
Идея в общем случае понятна, однако с реализацией возникли проблемы, можно привести полный код примера ?
Добавлено: 26 авг 2005, 12:55
ssDev
А вы ничего не путаете случаем?
Вообщето последовательности действий которые необходимо выполнить это не сурогат, а некоторая операция и по правилам ООП (в данном случае) должна являтся методом этого или производного класса.
И тогда нет необходимости в таких сурогатах.
Я бы использовал методы (лудьше всего виртуальные), для ветвлений.
Тогда отпадает проблема доступа к защищенным методам.