Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Только для извращенцев! Головоломка, а может и нет... классы, методы, поля 
:(
    Опции темы
BreakPointMAN
  Дата 4.7.2005, 07:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 15.9.2004
Где: Saratov

Репутация: 14
Всего: 24



Доброе время суток всем!

Вообщем, дружан подкинул мне такую задачку...
Представим, что у нас имеется класс:
Код

class A
   {
    private:
       int data;
    public:
       A():data(0){};

       int getData() {return data;}
       void setData(int _data) {data=_data;}
   }

Надеюсь, что затруднений особых он не вызовет.
Вот, каким образом мы обращаемся к переменной data? Через функции доступа - getData и setData, т.е. примерно так:
Код

A x;
x.setData(2005);
cout<<x.getData();

или так:
Код

A* y=new A;
y->setData(2005);
cout<<y->getData();

Товарищу моему такой вариант не слишком понравился...

Говорит, что не наглядно и не слишком красиво... Хотелось бы видеть что-то вроде:
Код

x.data=2005;
y->data=2005;

Ну, говорю, тогда помести переменную data в public:-секцию и не мучайся!..
Но тут возникает одно НО: нужен контроль за тем, что присваивается нашей переменной... Ну, если мы используем функцию доступа, то все просто:
Код

...
void setData(int _data)
   {
    if (f(_data))     data=_data;
   }
...

Тут f - это функция, принимающая int и возвращающая bool, а в функции может быть записано какое-то условие, ну, например,
Код

bool f(int arg) {if (arg%2==0) return true; else return false;}

Разрешит изменение переменной data только в том случае, если мы захотим присвоить ей четное число; если число будет нечетным - то переменная меняться не должна.

Вопрос в том, как сделать это, чтобы синтаксис работы с классом был такой:

A a;
a.data=10; // переменная изменится и станет равной 10
a.data=-7; // переменная не изменится, и останется

равной 10

???
Добавлено @ 07:42
Помучавшись немного, я пришел к следующему варианту... НО УСПОКОЙТЕ МЕНЯ - Я ИЗВРАЩЕНЕЦ ИЛИ НЕТ?! %)) Или можно проще?!
Код

// Функция f - какое-нибудь условие...
// Например, эта -  возвращает истину только для четных чисел,
// для нечетных ложь... сия функция будет вызываться при попытке
// присвоить переменной в нашем классе некоего значения,
// и именно от результата, возвращенного данной функцией, будет
// зависеть, будет ли выполнено присваивание или нет (в последнем случае
// значение переменной не изменится)

bool f(int arg) {if (arg%2==0) return true; else return false;}


int main(int argc, char* argv[])
{
        class A
           {

             /*    Определяем локальный класс, в котором, собственно и
                будет храниться "настоящая" переменная, содержащая наши
                реальные данные   */

             class DATA
                {
                 private:
                    int realdata; //  <-- вот где теперь хранится значение    [1]
                 public:

                    //   [2]   Каким значением, по умолчанию, должна быть инициализирована
                    // наша переменная? Это определяет конструктор по умолчанию:

                    DATA():realdata(0){};


                    //   [3]   Конструктор, принимающий целочисленное значение и инициализирующий
                    // им нашу переменную, нужен для того, чтобы можно было в дальнейшем
                    // создать _безымянный_ объект типа DATA, когда будет выполняться
                    // операция присваивания, в правой части которой будет целочисленное
                    // значение

                    DATA(int _DATA){realdata=_DATA;}


                    //     [4]   Следующая оператор-функция является операцией преобразования:
                    // в тех случаях, когда требуется значение типа int, а на деле используется
                    // типа DATA, эта функция вернет значение нашей переменной:

                    operator int() const {return realdata;}


                    //     [5]  А вот, собственно, и добрались до самого главного. Именно
                    // перегруженная операция присваивания позволит нам осуществить задуманное.
                    // Она будет вызываться каждый раз, когда мы имеем примерно такой код:
                    //
                    //      A* a=new A;
                    //      a->data=10;        <-- вот тут будет вызвана операция присваивания,
                    //                             но перед этим будет создан безымянный объект
                    // типа DATA, значением поля realdata которого является 10. Создание такого
                    // объекта нам обеспечит конструктор [3], определенный выше. И уже он будет
                    // являться вторым параметром операции присваивания (т.е. аргументом rhs;
                    // а первый параметр - неявный, это есть сам объект, для которого выполняется
                    // присваивание).

                    DATA& operator=(const DATA& rhs)
                       {
                        if(this==&rhs) return *this;
                        if(f(rhs)) realdata=rhs;    // <-- вызов функции f, именно от возвращенного
                        return *this;               //     ей результата (true/false) будет зависеть,
                                                    //     изменится или нет значение нашей
                                                    //     переменной. Функция должна принимать
                                                    //     значение типа int (т.е. такое же,
                                                    //     как и у нашей переменной), а возвращать
                                                    //     значение bool;
                                                    // кроме того, здесь у нас работает операция
                                                    // преобразования [4] - иначе мы не могли бы
                                                    // выполнить присваивание realdata=rhs,
                                                    // т.к. rhs имеет тип DATA&, а realdata -
                                                    // тип int... да и вызвать функцию f тоже бы
                                                    // так не смогли...

                       }

                };

            public:
               DATA data; // ну якобы наше поле... %)))
           };


        // ********************************************************************
        // До сих пор у нас было определение класса A. А теперь тестим...

        A a; // по умолчанию data == 0

        a.data=148; // четное число присвоено будет
        cout <<"a.data == " << a.data <<"\n";

        a.data=-17; // а нечетное нет... останется тем же, чтобы было присвоено раньше (148)
        cout <<"a.data == " << a.data <<"\n";


        // Можно создавать и динамически:

        A* b=new A;

        b->data=53; // нечетное число присвоено не будет, по умолчанию data было ==0
                    // таким же и останется
        cout <<"b->data == " << b->data <<"\n";



        b->data=-200;  // а четное - спокойно присвоится
        cout <<"b->data == " << b->data <<"\n";


        getch();
        return 0;
}



--------------------
"Разруха не в клозетах, а в головах." © Ф.Ф. Преображенский (М.Булгаков, "Собачье сердце")
PM WWW ICQ   Вверх
Earnest
Дата 4.7.2005, 08:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 53
Всего: 183



В общем, все примерно так и делается, когда нужно обеспечить контроль за внутренними переменными, прозрачный для клиентского кода. Если не ощибаюсь, это паттерн "прокси".
Однако конкретный случай тем не менее является извращением: нельзя так грубо менять семантику оператора присваивания. Когда программист видит в коде выражение a = b, то он имеет право предполагать, что либо присваивание произойдет, либо будет исключение. А тут - либо присвоит, либо нет ... Такого рода действия нужно прямо выражать в коде, и не оператором, а функцией с "говорящим" названием типа TryToAssign. А ваш случай аналогичен переопределению оператора 'плюс' причем в смысле 'через раз поделим на 2'.


--------------------
...
PM   Вверх
Alastis
Дата 4.7.2005, 11:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 251
Регистрация: 15.11.2004
Где: Казахстан, Астана

Репутация: 4
Всего: 10



Property в C++:

Код

class CValue
{
private:
  int m_value;
public:
  __declspec(property(get=getValue, put=setValue)) int Value;
  int getValue()
  {
    return m_value;
  }
  void setValue(int value)
  {
    m_value = value;
  }
};

....


CValue V;

V.Value = 1; // V.setValue(1);



взято отсюда



--------------------
Прости, что я говорю, когда ты меня перебиваешь.
PM MAIL WWW ICQ   Вверх
BreakPointMAN
Дата 4.7.2005, 13:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 15.9.2004
Где: Saratov

Репутация: 14
Всего: 24



Цитата(Earnest @ 4.7.2005, 08:54)
В общем, все примерно так и делается, когда нужно обеспечить контроль за внутренними переменными, прозрачный для клиентского кода. Если не ощибаюсь, это паттерн "прокси".

ммм... наверное, только, не "прокси", а "property"... %) мде... вообщем, я изобрел велосипед! %))) Ну ничего, зато интересно было...
Цитата(Earnest @ 4.7.2005, 08:54)
Однако конкретный случай тем не менее является извращением: нельзя так грубо менять семантику оператора присваивания. Когда программист видит в коде выражение a = b, то он имеет право предполагать, что либо присваивание произойдет, либо будет исключение. А тут - либо присвоит, либо нет ... Такого рода действия нужно прямо выражать в коде, и не оператором, а функцией с "говорящим" названием  типа TryToAssign. А ваш случай аналогичен переопределению оператора 'плюс' причем в смысле 'через раз поделим на 2'.

Вообще говоря, да, я согласен. Но здесь функция f у него, насколько я понял, просто будет обеспечивать верификацию корректности задания значения св-ва, т.е. чтобы, например, число лежало в определенных границах и т.п. И ничего более... так что... в принципе... с натяжкой... думаю, что можно допустить этот вариант.

2 Alastis: спасибо за ссылку!


--------------------
"Разруха не в клозетах, а в головах." © Ф.Ф. Преображенский (М.Булгаков, "Собачье сердце")
PM WWW ICQ   Вверх
Earnest
Дата 5.7.2005, 18:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 53
Всего: 183



BreakPointMAN
Да, существует точка зрения, что иногда на красный свет идти можно (если никого нет и т.д.). Но в вопросах программирования я категорически против такой "избирательности". Проффесиональный программист от любителя отличается не только хорошим знанием языка, но и тем, что твердо знает: программы пишутся для людей, а не для компиляторов. Программиста из моей группы я бы за такой изыск просто убила, ей-богу. smile
Насчет того, как называется паттерн - не помню, может и property... я имела ввиду паттерны от "банды четырех".


--------------------
...
PM   Вверх
BreakPointMAN
Дата 5.7.2005, 21:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 15.9.2004
Где: Saratov

Репутация: 14
Всего: 24



Несмотря на то, что уже есть инфа о том, что есть паттерн "свойство", и как это дело реализовано в MS VC и BCB, ... я продолжил изобретение велосипеда... извращенец я всё-таки... мде... smile
- вынес все в отдельный класс
- добавил параметризацию, сделав класс шаблонным
- добавил специализацию для типа char*
- ввел в интерфейс класса функтор - указатель на функцию проверки условия, должно присваиваться или нет... в принципе, если условиям r-value не подходит, можно выбрасывать исключение - так вроде правильнее будет...

Короче, хз, что получилось, ... обычный класс-контейнер, по сути... smile
Бред...
но всё же выложу...
уууух, и багов тут наверное!!! (хотя вроде проверял, работать должно)
Код

#ifndef __PROPERTY_H
#define PROPERTY_H
template <class T>
class PROPERTY
   {
    private:
       T data;
    public:
       PROPERTY();
       PROPERTY(T _data);
       operator T() const;
       PROPERTY (const PROPERTY&);
       PROPERTY& operator=(const PROPERTY& rhs);
       ~PROPERTY();
       bool(*Condition)(T);

   };
#endif
#ifndef __PROPERTY_CPP
#define PROPERTY_CPP


// шаблон - конструктор по умолчанию
template <class T>
   PROPERTY<T>::PROPERTY():Condition(NULL){/*data(NULL)*/};

// шаблон - конструктор, принимающий параметр типа T
template <class T>
   PROPERTY<T>::PROPERTY(T _data):Condition(NULL){data=_data;}

// шаблон - операция преобразования
template <class T>
   PROPERTY<T>::operator T() const
      {return data;}

// шаблон - операция присваивания
template <class T>
   PROPERTY<T>& PROPERTY<T>::operator=(const PROPERTY<T>& rhs)
      {
       if(this==&rhs) return *this;
       if(Condition==NULL)
          {
           data=rhs.data;
          }
       else
          {
           if(Condition(rhs.data)) data=rhs.data;
          }
       return *this;
      }

// шаблон - конструктор копирования
template <class T>
   PROPERTY<T>::PROPERTY(const PROPERTY<T>&):Condition(NULL){};

// шаблон - деструктор
template <class T>
   PROPERTY<T>::~PROPERTY(){}

// **********       Специализация для char *        **********



// char*  - конструктор по умочанию
PROPERTY<char*>::PROPERTY():Condition(NULL)
   {
    data=new char[1]; data[0]='\0';
   }

// char* - конструктор, принимающий строку
PROPERTY<char*>::PROPERTY(char* _data):Condition(NULL)
   {
    int tmp=strlen(_data);
    data=new char[tmp+1];
    strcpy(data, _data);
    data[tmp]='\0';
   }

// char* - операция присваивания
   PROPERTY<char*>& PROPERTY<char*>::operator=(const PROPERTY<char*>& rhs)
      {
       if(this==&rhs) return *this;
       if(Condition==NULL)
          {
           delete data;
           int tmp=strlen(rhs.data);
           data=new char[tmp+1];
           strcpy(data,rhs.data);
           data[tmp]='\0';
           }
       else
          {
           if(Condition(rhs.data))
              {
               delete data;
               int tmp=strlen(rhs.data);
               data=new char[tmp+1];
               strcpy(data,rhs.data);
               data[tmp]='\0';
             }
          }
       return *this;
      }


// char* - деструктор
PROPERTY<char*>:: ~PROPERTY()
   {
    delete data;
   }

// char* - конструктор копирования
PROPERTY<char*>::PROPERTY(const PROPERTY<char*>& rhs):Condition(NULL)
   {
    data=NULL;
    operator=(rhs);
   }
#endif

Добавлено @ 21:56
Юзается примерно так:

Код


// Определяем св-во x - работает как целочисленная переменная
PROPERTY<int> x;

//    Определяем функцию, накладывающую условие на присваиваемое значение,
// в данном  случае оно должно быть в диапазоне [-10;10]
bool f(int arg)
   {
       if(arg<-10 || arg >10)
          {
           cout <<"\nRange error!\n";
           return false;
          }
       else
          return true;
   } 

// Устанавливаем это условие на наше св-во x
x.Condition=f; 

x=5; // присвоится
x=-5; // присвоится
x=14; // не присвоится




--------------------
"Разруха не в клозетах, а в головах." © Ф.Ф. Преображенский (М.Булгаков, "Собачье сердце")
PM WWW ICQ   Вверх
BreakPointMAN
Дата 5.7.2005, 22:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 15.9.2004
Где: Saratov

Репутация: 14
Всего: 24



PS: для стандартных типов типа char, int, float, double, а также char* должно работать без проблем... а для остальных - надо чтобы в классах, которые типом будут, были корректно определены конструкторы (по умолчанию и копирования), операция присваивания и деструктор.
Добавлено @ 22:07
Цитата(Earnest @ 5.7.2005, 18:26)
BreakPointMAN
Да, существует точка зрения, что иногда на красный свет идти можно (если никого нет и т.д.). Но в вопросах программирования я категорически против такой "избирательности". Проффесиональный программист от любителя отличается не только хорошим знанием языка, но и тем, что твердо знает: программы пишутся для людей, а не для компиляторов. Программиста из моей группы я бы за такой изыск просто убила, ей-богу. smile
Насчет того, как называется паттерн - не помню, может и property... я имела ввиду паттерны от "банды четырех".

а то была, вообще, не моя идея... ))
меня спросили - как сделать можно...
ну и заинтересовало...
good, что это pattern такой есть, good, что уже давно написан, проверен и отлажен и под BCB, и под MSVC,
потыркаться все равно прикольно было.
вот... )


--------------------
"Разруха не в клозетах, а в головах." © Ф.Ф. Преображенский (М.Булгаков, "Собачье сердце")
PM WWW ICQ   Вверх
BreakPointMAN
Дата 5.7.2005, 22:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 15.9.2004
Где: Saratov

Репутация: 14
Всего: 24



Кстати, народ, может посоветуете что-нибудь по паттернам почитать, кто знает? smile
Хотелось бы побольше про всякие фишки узнать...
Монотипы, синглетоны... бррр... smile


--------------------
"Разруха не в клозетах, а в головах." © Ф.Ф. Преображенский (М.Булгаков, "Собачье сердце")
PM WWW ICQ   Вверх
Fantasist
Дата 5.7.2005, 22:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй
***


Профиль
Группа: Участник Клуба
Сообщений: 1517
Регистрация: 24.3.2002

Репутация: 4
Всего: 41



Цитата(BreakPointMAN @ 4.7.2005, 10:35)
ммм... наверное, только, не "прокси", а "property"... %) мде... вообщем, я изобрел велосипед! %))) Ну ничего, зато интересно было...


Что-то не помню я такого паттерна "property". smile Property к design patterns (шаблоны проектирования) в классическом варианте не имеет отношения. Property - это концепция контролируемого доступа к неким данным в виде обращения к переменной. В разных языках (и средах) она реализуется по разному, а иногда вообще не реализуется. Так например в Delphi или C# она встроенна в сам язык (понятно, что в этом случае ничего проектировать не надо). В VC и C++ Builder'e есть расширения языка реализующие property (как раз этот __declspec для VC). В Java реализация proprty вообще, насколько я знаю, невозможна, а вот как раз в стандартном С++ это можно реализовать именно так, как это сделал ты. То есть я бы тоже не использовал всякие расширения типа __declspec, а пользовал бы переносимый вариант. Однако, я пока не видел более менее красивого способа автоматизировть этот процесс. Вот что-то вроде такого:

Код

template<class VType, VType& (*setF)(VType&,VType&), VType& (*getF)(VType&)>
class prop
{
private:
   VType m_data;
public:
   prop() {};
   prop(VType data): m_data(value) {}; 

   operator VType() {return getF(m_data); };
   VType& operator=(VType& value) {return setF(m_data,value); };
};

class A
{
public:
    static int& SetA(int& data, int& value) {return data = value;};
    static int& GetA(int& data) {return data;};
    prop<int,SetA,GetA> n;    

};


int main()
{
    A a;
    int i = a.n;
    g.n = i;

    return 0;
}





--------------------
Волны гасят ветер...
PM MAIL   Вверх
Fantasist
Дата 5.7.2005, 22:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй
***


Профиль
Группа: Участник Клуба
Сообщений: 1517
Регистрация: 24.3.2002

Репутация: 4
Всего: 41



Цитата(BreakPointMAN @ 5.7.2005, 19:12)
Кстати, народ, может посоветуете что-нибудь по паттернам почитать, кто знает?


Например здесь
Добавлено @ 22:49
Хотя property и не относится к шаблонам проекитрования. smile


--------------------
Волны гасят ветер...
PM MAIL   Вверх
Vyacheslav
Дата 6.7.2005, 13:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 9
Всего: 59



Property

Присоединённый файл ( Кол-во скачиваний: 32 )
Присоединённый файл  Property.pdf 57,92 Kb


--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
BreakPointMAN
Дата 6.7.2005, 16:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 15.9.2004
Где: Saratov

Репутация: 14
Всего: 24



2Fantasist & Vyacheslav: спасибо! smile


--------------------
"Разруха не в клозетах, а в головах." © Ф.Ф. Преображенский (М.Булгаков, "Собачье сердце")
PM WWW ICQ   Вверх
Fantasist
Дата 6.7.2005, 23:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй
***


Профиль
Группа: Участник Клуба
Сообщений: 1517
Регистрация: 24.3.2002

Репутация: 4
Всего: 41



Цитата(Vyacheslav @ 6.7.2005, 10:05)
Property


К сожалению, ничего нового не увидел. Что мне не нравится присутвует и здесь - надо передавать тип класса в котором эта проперти используется для каждой проперти.

Это сообщение отредактировал(а) Fantasist - 7.7.2005, 00:17


--------------------
Волны гасят ветер...
PM MAIL   Вверх
Vyacheslav
Дата 8.7.2005, 09:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 9
Всего: 59



Цитата(Fantasist @ 5.7.2005, 22:36)
В Java реализация proprty вообще, насколько я знаю, невозможна

Как это так? Там кажется есть соглашение. Для определения проперти XXX нужно определить хотя бы один из методов getXXX и setXXX


--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
comcon1
Дата 12.7.2005, 12:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 838
Регистрация: 11.6.2005
Где: Москва ДАС-МГУ

Репутация: 12
Всего: 17



А почему property не вносят в стандарт языка?? Ведь в Borland-e вполне себе работает. И синтаксис очень красивый. smile

Или оно порождает некие глюки, простым смертным не видные?? Или не согласуется с концепцией какой-нить??


--------------------
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0700 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.