Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

"Триггер" в классе

11
02 апреля 2008 года
oxotnik333
2.9K / / 03.08.2007
Можно ли сделать некий метод класса, что бы он вызывался автоматически при обращению к указателю класса, т.е.:
Код:
class SomeClass
{
   public:
   SomeClass();

   int Var1;
   int Var2;
........
   private:

  void TRIGGER (void);
....
};

SomeClass *pSomeClass;

void SomeFunc (void)
{
  pSomeClass->Var1;
}

перед тем как вызовется pSomeClass->Var1 надо автоматом запустить void TRIGGER (void); (для того что бы в Var1 попали "свежие данные")
1.8K
02 апреля 2008 года
_const_
229 / / 26.11.2003
 
Код:
class SomeClass
{
...
SomeClass* operator->() { TRIGGER(); return this; };
};
11
02 апреля 2008 года
oxotnik333
2.9K / / 03.08.2007
Цитата: _const_
 
Код:
class SomeClass
{
...
SomeClass* operator->() { TRIGGER(); return this; };
};



т.е. если написать так:

 
Код:
void SomeFunc (void)
{
  pSomeClass;
}

то в классе автоматом сработает TRIGGER(); ?
87
02 апреля 2008 года
Kogrom
2.7K / / 02.02.2008
Интересно. Я в первый раз увидел, где может пригодиться перегрузка оператора -> :) Однако теперь получается, что при вызове любой переменной или функции класса с помощью -> будет запускаться TRIGGER();

Если это не подходит, то наверно лучше использовать функции типа SetVar1(), GetVar1() и уже в них вызывать TRIGGER(); ?
1.8K
02 апреля 2008 года
_const_
229 / / 26.11.2003
Цитата: oxotnik333
т.е. если написать так:
 
Код:
void SomeFunc (void)
{
  pSomeClass;
}

то в классе автоматом сработает TRIGGER(); ?



Если написать так, то компилятор выдаст ошибку.

11
02 апреля 2008 года
oxotnik333
2.9K / / 03.08.2007
Цитата: _const_
Если написать так, то компилятор выдаст ошибку.


Борланд не выдает... да и чего тут неправильного?

вот че получилось, но не работает:

Код:
class SomeClass
{

 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
87
02 апреля 2008 года
Kogrom
2.7K / / 02.02.2008
Button2->Caption = (*pSomeClass)->var1;
Хотя это похоже на изврат :)
11
02 апреля 2008 года
oxotnik333
2.9K / / 03.08.2007
Цитата: Kogrom
Button2->Caption = (*pSomeClass)->var1;
Хотя это похоже на изврат :)



так работает :)

1.8K
02 апреля 2008 года
_const_
229 / / 26.11.2003
Цитата: oxotnik333
Борланд не выдает... да и чего тут неправильного?


То, что написанное здесь отличается от предыдущего поста. Напиши Борланду именно ту строку:

 
Код:
void Func()
{
pSomeClass;
}

Что, не выдает ошибку? Не верю.

Цитата: oxotnik333

вот че получилось, но не работает:
Код:
class SomeClass
{

 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



И не должно. Рабочий вариант:

 
Код:
CSomeClass some;
Caption = some->var1;

Правда не знаю, насколько оно соответствует тому, что надо получить.
11
02 апреля 2008 года
oxotnik333
2.9K / / 03.08.2007
Цитата: _const_
То, что написанное здесь отличается от предыдущего поста. Напиши Борланду именно ту строку:
 
Код:
void Func(void /*тут только поменял*/)
{
pSomeClass;
}

Что, не выдает ошибку? Не верю.



не выдает

Цитата: _const_

И не должно. Рабочий вариант:
 
Код:
CSomeClass some;
Caption = some->var1;

Правда не знаю, насколько оно соответствует тому, что надо получить.



Относительно того что надо получить:
Хочу написать класс-обертку для работы с БД, которым можно будет пользоваться, не заморачиваясь на обновление данных. Т.е. при каждом обращении к этому классу (при каждом вызове членов и методов класса) на выходе будут только "свежие" данные на момент вызова.
Т.о. юзер у класса запрашивает член var1, класс автоматом производит некие действия с БД и помещает свежее значение в var1.
ЗЫ: вот так:
CSomeClass some;
Caption = some->var1;
не совсем удобно юзать обетку, удобней (для юзера) будет создать указатель на класс и от этого указателя работать
SomeClass *pSomeClass= new SomeClass();
далее работа сводиться по хорошему должна только к указателю pSomeClass.

ЗЫЗЫ: параллельно вопрос вылез: как в теле void TRIGGER(void) узнать какой член класса запросил пользователь (если такой возможно)?

3
02 апреля 2008 года
Green
4.8K / / 20.01.2000
Цитата: Kogrom
Интересно. Я в первый раз увидел, где может пригодиться перегрузка оператора -> :)


Ну а как же "умные указатели"?
Что собственно и нужно автору топика.

oxotnik333, тебе нужно создать свой "умный указатель", т.е. дополнительный класс, который будет управлять доступом к твоему классу SomeClass.
Дополнительно, чтобы случайно не создать обходных путей доступа к SomeClass, запрети прямое создание его экземпляров, сделав конструктор этого класса приватным, и создав фабрику объектов другом этого класса. Фабрика может быть частью "умного указателя".

87
02 апреля 2008 года
Kogrom
2.7K / / 02.02.2008
Цитата: Green
Ну а как же "умные указатели"?



Подобный термин я видел только в содержании книги Джеффа Элджера. Однако я побоялся её читать, когда во вступлении прочитал:

Цитата:
"Эта книга — совсем другое дело. Прежде всего, она предполагает, что вы уже владеете С++. Вероятно, вы программировали на С++ в течение года-двух или более."

В то время как я программирую на c++ только пол года. Еще немного "вырасту" и прочитаю...

11
02 апреля 2008 года
oxotnik333
2.9K / / 03.08.2007
проще всего сделать так:
Код:
class SomeClass
{
void TRIGGER (void);
...
int get_var1(void);
int get_var2(void);
и т.д.

};

int SomeClass::get_var1(void)
{
TRIGGER();
....
return var1;
}


но это не красиво.
А вот через фабрики идея то что надо, а главное расширяемая оболочка получится
3
02 апреля 2008 года
Green
4.8K / / 20.01.2000
Если теье просто надо обращаться к полям класса, то это лучше делать через get/set, а сами поля приватными. Вообще, прямое оперирование с полями класса извне - плохой стиль.

Если же тебе надо "мониторить" обращение к классу, то это, как уже говорил, лучше сделать через "умные указатели", т.к. "мониторинг" не должен быть частью самого класса.

Фабрику я упомянул лишь для того, чтоб исключить возможность создания экземпляра не "обернутого" умным указателем.
11
02 апреля 2008 года
oxotnik333
2.9K / / 03.08.2007
Цитата: Green
Если теье просто надо обращаться к полям класса, то это лучше делать через get/set, а сами поля приватными. Вообще, прямое оперирование с полями класса извне - плохой стиль.



это типа того, что было моим постом выше?

3
02 апреля 2008 года
Green
4.8K / / 20.01.2000
Цитата: oxotnik333
это типа того, что было моим постом выше?


Да, т.е.
int get_var1();
и соотв-но
void set_var1(int);

17K
03 апреля 2008 года
HookEst
144 / / 27.03.2008
Несомненно get_xxx set_xxx вариант самый практичный, и именно его и надо использовать на практике. Но если очень уж хочется...

все что ниже - это просто мысли перед сном
тем более нужен не простой мониторинг, а нужно чтобы сам класс реагировал на доступ к его полям(с методами то все ясно).

Обертка над всем классом(какой-нибудь SmartPointer) позволит перехватить обращения к классу, но не обращения к конкретным полям.
Цитата: oxotnik333

...
ЗЫЗЫ: параллельно вопрос вылез: как в теле void TRIGGER(void) узнать какой член класса запросил пользователь (если такой возможно)?


так может сделать отдельно обертки для каждого поля?
Если мы перегрузим оператор присваивания для него, то сможем перехватить событие onSetProperty, если перегрузим оператор приведения к типу поля - перехватим onGetProperty. Конечно, этого маловато, не во всех случаях сработает, но мы можем еще много чего перегрузить)).
Подумать только как эти обертки покрасивее реализовать(классами, шаблонами, макросами...) и как информировать сам класс о доступе к его полям.
Вот что у меня получилось, можь кому интересно...
просто с макросом и friend class...(MSVC++2003)

Код:
#include "stdafx.h"

#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;
}

простые операции вполне перехватываются, ну иногда нужно явное приведение типа.
Спасибо за внимание))
3
03 апреля 2008 года
Green
4.8K / / 20.01.2000
Зачем такие сложности?
Кроме загромождения кода, добавляются неявности.
Если уж ты конструируешь класс с таким расчетом, чтоб обрабатывать обращение к его полям, то создавай гетеры и сетеры, и перегружай операции доступа, если уж потребуется.
Если ты используешь уже готовый клас и хочешь мониторить обращение к нему, то оборачивай.
Но городить такой огород, как показано выше... зачем?
17K
04 апреля 2008 года
HookEst
144 / / 27.03.2008
незачем, практически это абсолютно не нужно, но теоретически возможность есть
255
04 апреля 2008 года
Dart Bobr
1.4K / / 09.04.2004
Я вот тут на тему гетеров и сетеров размышлял..
Есть у него свои плюсы - например, гарантирование корректности присваивания. Например, если я пишу класс контейнер и его структура примерно такая:
 
Код:
class Container
{
  private:
     int Length;
     char * buffer;
  ......
}

То мне выгодно пользоваться аксесорами, так как может понадобиться перевыделить память для поля buffer и т.д. и т.п.
Но, есть один серьезный минус - еси мне нужно передать данные из другого класса в этот, то я имею два способа:
1. использовать промежуточную переменную, например так:
 
Код:
char * pbuf = new char [MAX_NEEDED_SIZE];
Someclass.getdata(pbuf);
container.setData(pbuf);

Чем плохо - лишния операция копирования. Если большой размер данных передается - может существенно снизить скорость работы программы.
2. Обьявить второй класс как friend первого, и обращаться к данным первого напрямую. Это решение лучше первого тем, что не нужно лишний раз копировать данные. Но, тоже имеет ряд недостатков - и первый и на мой взгляд, самый значительный из них, - наследование дружественного класса.
Как бы это можно было б реализовать, чтоб и вроди как напрямую обращаться к памяти, и сделать такие обращения "защищенными", то-есть чтоб за пределы памяти не вылазили, и удобными? Пока-что ничего интересного не придумал (
3
04 апреля 2008 года
Green
4.8K / / 20.01.2000
Dart Bobr, ты сам себя запутал. Потому, что не написал, как у тебя будут выглядеть аксессоры.
А выглядеть они должны примерно так:
const char* getdata() const;
void setdata(const char*);
ничего промежуточного не надо.
255
04 апреля 2008 года
Dart Bobr
1.4K / / 09.04.2004
Green, ты не совсем меня понял)
Я имею ввиду, что мне нужно использовать промежуточный буфер, если я хочу получить данные из какой-либо функции, особенно из апи-функции, которые любят возвращать данные как параметр, и засунуть это в класс контейнера)
Например, я использую библиотеку, которая извлекает мне содержимое архива таким образом:
int GetDataFromArchive(int index, char* data);
И мне придется использовать промежуточный буфер для обвертки скажем этих данных в приведенный выше класс контейнера.
3
04 апреля 2008 года
Green
4.8K / / 20.01.2000
Проблема в том, что ты пытаешься совместить ООП часть и не ООП часть:
твой класс - это ООП, а вот приведенный вызов функции - это не ООП.
Если рассуждать с т.з. ООП, то это решаемо как-минимум тремя способами. Сразу буду показывать их на привычных примерах.

1. Оперирование объектами.
 
Код:
cin >> str;
v.push_back(str);

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

2. Кроме get и set, ещё частенько бывает assign или т.п.:
 
Код:
class Container
{
public:
    void assign(char* buffer) {
        this->buffer = buffer;
   }

private:
     char* buffer;
}

но сама по себе идея оперировать char* вне класса плоха, т.к. это все же не ООП-стиль в совмещении с ООП. Поэтому переходим к следующему варианту

3. Оборачиваем всё не ООП в ООП. Это можно сделать несколькими способами.
1) т.к. твой контейнер сам по себе без данных имеет мало смысла, то логично его создавать уже с данными:
Код:
class Container
{
public:
    Container* create(index) {
        container = new Container;
        GetDataFromArchive(index, container->buffer);
        return container;
    }

private:
    char* buffer;

    Container();
}

2) если способов заполнения контейнеров много, давай созданим "заполнители" контейнера:
 
Код:
void getDataFromArchive(int index, Container& container) {
    GetDataFromArchive(index, data);
    container.assign(data);
}

но наверное самым правильным способом будет следующий:
Код:
class Container
{
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);
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог