"Триггер" в классе
{
public:
SomeClass();
int Var1;
int Var2;
........
private:
void TRIGGER (void);
....
};
SomeClass *pSomeClass;
void SomeFunc (void)
{
pSomeClass->Var1;
}
перед тем как вызовется pSomeClass->Var1 надо автоматом запустить void TRIGGER (void); (для того что бы в Var1 попали "свежие данные")
{
...
SomeClass* operator->() { TRIGGER(); return this; };
};
{
...
SomeClass* operator->() { TRIGGER(); return this; };
};
т.е. если написать так:
{
pSomeClass;
}
то в классе автоматом сработает TRIGGER(); ?
Если это не подходит, то наверно лучше использовать функции типа SetVar1(), GetVar1() и уже в них вызывать TRIGGER(); ?
{
pSomeClass;
}
то в классе автоматом сработает TRIGGER(); ?
Если написать так, то компилятор выдаст ошибку.
Борланд не выдает... да и чего тут неправильного?
вот че получилось, но не работает:
{
public:
SomeClass(void);
int var1;
SomeClass* operator->() { TRIGGER(); return this; };
void TRIGGER (void);
};
SomeClass *pSomeClass;
SomeClass::SomeClass(void)
{ var1 = 0; }
void SomeClass::TRIGGER(void)
{ var1++; }
void __fastcall TForm1::Button1Click(TObject *Sender)
{
pSomeClass = new SomeClass();
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Button2->Caption = pSomeClass->var1;
}
Button2->Caption всегда == 0
Хотя это похоже на изврат :)
Хотя это похоже на изврат :)
так работает :)
То, что написанное здесь отличается от предыдущего поста. Напиши Борланду именно ту строку:
{
pSomeClass;
}
Что, не выдает ошибку? Не верю.
вот че получилось, но не работает:
{
public:
SomeClass(void);
int var1;
SomeClass* operator->() { TRIGGER(); return this; };
void TRIGGER (void);
};
SomeClass *pSomeClass;
SomeClass::SomeClass(void)
{ var1 = 0; }
void SomeClass::TRIGGER(void)
{ var1++; }
void __fastcall TForm1::Button1Click(TObject *Sender)
{
pSomeClass = new SomeClass();
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Button2->Caption = pSomeClass->var1;
}
Button2->Caption всегда == 0
И не должно. Рабочий вариант:
Caption = some->var1;
Правда не знаю, насколько оно соответствует тому, что надо получить.
{
pSomeClass;
}
Что, не выдает ошибку? Не верю.
не выдает
И не должно. Рабочий вариант:
Caption = some->var1;
Правда не знаю, насколько оно соответствует тому, что надо получить.
Относительно того что надо получить:
Хочу написать класс-обертку для работы с БД, которым можно будет пользоваться, не заморачиваясь на обновление данных. Т.е. при каждом обращении к этому классу (при каждом вызове членов и методов класса) на выходе будут только "свежие" данные на момент вызова.
Т.о. юзер у класса запрашивает член var1, класс автоматом производит некие действия с БД и помещает свежее значение в var1.
ЗЫ: вот так:
CSomeClass some;
Caption = some->var1;
не совсем удобно юзать обетку, удобней (для юзера) будет создать указатель на класс и от этого указателя работать
SomeClass *pSomeClass= new SomeClass();
далее работа сводиться по хорошему должна только к указателю pSomeClass.
ЗЫЗЫ: параллельно вопрос вылез: как в теле void TRIGGER(void) узнать какой член класса запросил пользователь (если такой возможно)?
Ну а как же "умные указатели"?
Что собственно и нужно автору топика.
oxotnik333, тебе нужно создать свой "умный указатель", т.е. дополнительный класс, который будет управлять доступом к твоему классу SomeClass.
Дополнительно, чтобы случайно не создать обходных путей доступа к SomeClass, запрети прямое создание его экземпляров, сделав конструктор этого класса приватным, и создав фабрику объектов другом этого класса. Фабрика может быть частью "умного указателя".
Подобный термин я видел только в содержании книги Джеффа Элджера. Однако я побоялся её читать, когда во вступлении прочитал:
В то время как я программирую на c++ только пол года. Еще немного "вырасту" и прочитаю...
{
void TRIGGER (void);
...
int get_var1(void);
int get_var2(void);
и т.д.
};
int SomeClass::get_var1(void)
{
TRIGGER();
....
return var1;
}
но это не красиво.
А вот через фабрики идея то что надо, а главное расширяемая оболочка получится
Если же тебе надо "мониторить" обращение к классу, то это, как уже говорил, лучше сделать через "умные указатели", т.к. "мониторинг" не должен быть частью самого класса.
Фабрику я упомянул лишь для того, чтоб исключить возможность создания экземпляра не "обернутого" умным указателем.
это типа того, что было моим постом выше?
Да, т.е.
int get_var1();
и соотв-но
void set_var1(int);
все что ниже - это просто мысли перед сном
тем более нужен не простой мониторинг, а нужно чтобы сам класс реагировал на доступ к его полям(с методами то все ясно).
Обертка над всем классом(какой-нибудь SmartPointer) позволит перехватить обращения к классу, но не обращения к конкретным полям.
...
ЗЫЗЫ: параллельно вопрос вылез: как в теле void TRIGGER(void) узнать какой член класса запросил пользователь (если такой возможно)?
так может сделать отдельно обертки для каждого поля?
Если мы перегрузим оператор присваивания для него, то сможем перехватить событие onSetProperty, если перегрузим оператор приведения к типу поля - перехватим onGetProperty. Конечно, этого маловато, не во всех случаях сработает, но мы можем еще много чего перегрузить)).
Подумать только как эти обертки покрасивее реализовать(классами, шаблонами, макросами...) и как информировать сам класс о доступе к его полям.
Вот что у меня получилось, можь кому интересно...
просто с макросом и friend class...(MSVC++2003)
#define PROPERTY(propType,ownerClass,propName,propGetter,propSetter) \
class propName##PropertyClass{\
friend class ownerClass;\
ownerClass *_inst;\
propType _value;\
public:\
operator propType(){return _inst->propGetter();};\
propType operator=(propType rhs){return _inst->propSetter(rhs);};\
} propName;
#define INIT_PROP(propName) \
propName._inst=this
class CClass{
public:
PROPERTY(int,CClass,value,Get_value,Set_value);
PROPERTY(double,CClass,Num,GetNum,SetNum);
CClass(){
INIT_PROP(value);
INIT_PROP(Num);
}
private:
int Get_value(){
printf("You GET the value=%d\n",value._value );
return value._value;
};
int Set_value(const int rhs){
printf("You SET the value with %d\n",value._value=rhs);
return value._value;
};
double GetNum(){
printf("You GET the Num=%f\n",Num._value );
return Num._value;
};
double SetNum(double rhs){
printf("You SET the Num with %f\n",Num._value=rhs);
return Num._value;
};
};
int _tmain(int argc, _TCHAR* argv[])
{
CClass c;
c.value=10;
c.Num=0.01;
printf("c.value=%d\tc.Num=%f\n",(int)c.value,(double)c.Num);
printf("\n\n********\nwith pointer\n");
CClass*p=new CClass;
p->value=10;
int i=p->value;
p->value=i+10;
printf("value=%d\n\n",(int)p->value);
p->Num=8.5645;
double d=p->Num;
p->Num=p->value/7+p->Num;
printf("Num=%f\n\n",(double)p->Num);
delete p;
printf("Press ENTER\n");
getchar();
return 0;
}
простые операции вполне перехватываются, ну иногда нужно явное приведение типа.
Спасибо за внимание))
Кроме загромождения кода, добавляются неявности.
Если уж ты конструируешь класс с таким расчетом, чтоб обрабатывать обращение к его полям, то создавай гетеры и сетеры, и перегружай операции доступа, если уж потребуется.
Если ты используешь уже готовый клас и хочешь мониторить обращение к нему, то оборачивай.
Но городить такой огород, как показано выше... зачем?
Есть у него свои плюсы - например, гарантирование корректности присваивания. Например, если я пишу класс контейнер и его структура примерно такая:
{
private:
int Length;
char * buffer;
......
}
То мне выгодно пользоваться аксесорами, так как может понадобиться перевыделить память для поля buffer и т.д. и т.п.
Но, есть один серьезный минус - еси мне нужно передать данные из другого класса в этот, то я имею два способа:
1. использовать промежуточную переменную, например так:
Someclass.getdata(pbuf);
container.setData(pbuf);
Чем плохо - лишния операция копирования. Если большой размер данных передается - может существенно снизить скорость работы программы.
2. Обьявить второй класс как friend первого, и обращаться к данным первого напрямую. Это решение лучше первого тем, что не нужно лишний раз копировать данные. Но, тоже имеет ряд недостатков - и первый и на мой взгляд, самый значительный из них, - наследование дружественного класса.
Как бы это можно было б реализовать, чтоб и вроди как напрямую обращаться к памяти, и сделать такие обращения "защищенными", то-есть чтоб за пределы памяти не вылазили, и удобными? Пока-что ничего интересного не придумал (
А выглядеть они должны примерно так:
const char* getdata() const;
void setdata(const char*);
ничего промежуточного не надо.
Я имею ввиду, что мне нужно использовать промежуточный буфер, если я хочу получить данные из какой-либо функции, особенно из апи-функции, которые любят возвращать данные как параметр, и засунуть это в класс контейнера)
Например, я использую библиотеку, которая извлекает мне содержимое архива таким образом:
int GetDataFromArchive(int index, char* data);
И мне придется использовать промежуточный буфер для обвертки скажем этих данных в приведенный выше класс контейнера.
твой класс - это ООП, а вот приведенный вызов функции - это не ООП.
Если рассуждать с т.з. ООП, то это решаемо как-минимум тремя способами. Сразу буду показывать их на привычных примерах.
1. Оперирование объектами.
v.push_back(str);
Здесь так же есть промежуточный объект str, но по поводу его использования не возникает вопросов.
2. Кроме get и set, ещё частенько бывает assign или т.п.:
{
public:
void assign(char* buffer) {
this->buffer = buffer;
}
private:
char* buffer;
}
но сама по себе идея оперировать char* вне класса плоха, т.к. это все же не ООП-стиль в совмещении с ООП. Поэтому переходим к следующему варианту
3. Оборачиваем всё не ООП в ООП. Это можно сделать несколькими способами.
1) т.к. твой контейнер сам по себе без данных имеет мало смысла, то логично его создавать уже с данными:
{
public:
Container* create(index) {
container = new Container;
GetDataFromArchive(index, container->buffer);
return container;
}
private:
char* buffer;
Container();
}
2) если способов заполнения контейнеров много, давай созданим "заполнители" контейнера:
GetDataFromArchive(index, data);
container.assign(data);
}
но наверное самым правильным способом будет следующий:
{
public:
Container(const Container& other) {
copy(buffer, other.buffer, other.size);
}
Container(data, size) {
copy(buffer, data, size);
}
private:
char* buffer;
}
Container getDataFromArchive(int index) {
GetDataFromArchive(index, data);
return Container(data);
}
Собственно, далеко ходить не надо, так устроен std::string.
Т.о. мы вернулись к первому варианту (cin >> str);