Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Вопрос по организации ООП структуры программы.


Автор: KPEHDEL 31.7.2012, 17:15
Здраствуйте, написал класс Button обрабатывающий действия с кнопокой, наведение мыши на неё, нажатие, отжатие...

Информация:
Есть метод объявления данных кнопки, x, y, w, h, сурфейса, координат спрайтов.
data( int x, int y, int w, int h, SDL_Surface *Surface, SDL_Rect* sprite );
        
Есть метод обработки нажатия кнопки mouse_button_down().

В этом методе при нажатии кнопки, должен вызываться метод с названием кнопки из класса Event_Button. Например если обрабатывается объект-кнопка Button_Header_Menu_Enter то имя метода из класса Event_Button будет равно имени объекта.

Класс Event_Button содержит объект Button_Down и публичные методы имеющие название кнопок.
И в методе mouse_button_down() о котором я писал выше, вызывается например так:
Button_Event.название_метода();

Вопрос:
Как правильно в соответствии с ООП передать название метода, чтобы при нажатии на определённую кнопку вызывался метод с её именем из класса Button_Event, или как это вообще делается?
На C++, никогда не писал, ранее писал на C. Теперь встал на путь C++. 
Поэтому не всегда мне ясно как правильнее сделать в соответствии с ООП. Буду благодарен если направите мои мысли в нужное русло.

С нетерпением жду ответа, заранее спасибо.

Автор: bsa 31.7.2012, 17:32
Почитай: http://qt-project.org/doc/qt-4.8/gettingstarted-develop.html
Это введение в библиотеку Qt. Она написана довольно хорошим стилем и ее архитектура может быть использована как пример. Система сигналов/слотов, правда, там не самая удачная. Поэтому, в своем дизайне можешь использовать boost::signals.

Автор: baldina 31.7.2012, 17:40
Имена методов/классов/переменных в C++ недоступны в runtime (и вообще поддержка RTTI совсем небольшая), так что идентифицировать по имени без специальных бубнов не получится. Но и не надо: каждая кнопка сама знает, какой событие должно сгенерироваться, и просто вызывает соответствующий метод:

Код

class Event_Button {
  public:
    void Open ();
    void Save ();
    void Close ();
};

class Button {
  protected:
    Event_Button& e;
  public:
    Button (Event_Button& e) : e(e) {}
    virtual void mouse_button_down () = 0;
};

class OpenButton {
  public:
    OpenButton (Event_Button& e) : Button(e) {}
    virtual void mouse_button_down ()  {
       e.Open ();
    }
};

class SaveButton {
  public:
    SaveButton (Event_Button& e) : Button(e) {}
    virtual void mouse_button_down ()  {
       e.Save ();
    }
};
class CloseButton {
  public:
    CloseButton (Event_Button& e) : Button(e) {}
    virtual void mouse_button_down ()  {
       e.Close ();
    }
};


Добавлено @ 17:42
а вот если мы хотим связать произвольную кнопку с произвольным событием, потребуется какой-то механизм связывания. та самая система слотов/сигналов.

Автор: baldina 31.7.2012, 17:56
в простейшем виде вот так примерно
http://codepad.org/zNow66oD

Автор: KPEHDEL 31.7.2012, 18:01
Спасибо, всем ответившим. Направление в котором идти уловил. Буду читать, думать, позже отпишусь о результатах и о том, как сделал.

Автор: Result 31.7.2012, 21:19
Цитата(baldina @ 31.7.2012,  17:40)
Код

class Event_Button {
  public:
    void Open ();
    void Save ();
    void Close ();
};

class Button {
  protected:
    Event_Button& e;
  public:
    Button (Event_Button& e) : e(e) {}
    virtual void mouse_button_down () = 0;
};

class OpenButton {
  public:
    OpenButton (Event_Button& e) : Button(e) {}
    virtual void mouse_button_down ()  {
       e.Open ();
    }
};

class SaveButton {
  public:
    SaveButton (Event_Button& e) : Button(e) {}
    virtual void mouse_button_down ()  {
       e.Save ();
    }
};
class CloseButton {
  public:
    CloseButton (Event_Button& e) : Button(e) {}
    virtual void mouse_button_down ()  {
       e.Close ();
    }
};

Кажется забыли дописать паблик наследование.

Автор: baldina 1.8.2012, 11:16
Цитата(Result @  31.7.2012,  21:19 Найти цитируемый пост)
Кажется забыли дописать паблик наследование. 

да, забыли

Автор: KPEHDEL 2.8.2012, 22:13
Выбор мой пал всё же не на boost, а на старый добрый Указатель на член. Подходит под задачу и подключать ничего не надо. Но boost::signals скоро тоже понадобится.

Продолжу тему своими следущими вопросами, подходящими под оглавление темы.

1.
Информация:
Я использую конструктор/деструктор, но также когда требуется создаю метод init() в котором вызывается, то, что должно быть вызвано единажды, а не для каждого объекта.
Например:
SDL_EnableUNICODE( SDL_ENABLE ); - если вызывать в конструкторе, то после второго вызова для второго объекта, вернёт ошибку. Такое вызываю в методе init().

В конструкторе исключительно то, что требуется инициализировать для каждого объекта.

Вопрос:
Это нормально? Принято ли так делать в C++ или лучше использовать только конструктор?

2.
Информация:
Есть объекты класса с конструктором принмающим параметры.
Требуется объявить один объект, для которого параметры не нужны. По причине, что через этот объект нужно вызывать просто какой-то метод в методе другого класса.
Пример:
Где-то вызывается Button.Header_Menu();
А в Header_Menu() вызываются все остальные "нормальные" объекты с параметрами конструктора.
Поэтому, объекту проводнику параметры не нужны.

Вопрос:
В таком случаи делать Button Button( NULL, NULL, NULL, NULL, NULL, NULL, NULL );
или есть другие способы?

3.
Информация:

Есть конструктор принимающий параметр SDL_Surface* source и объект передающий его.
В конструкторе данный параметр присваивается члену класса ClassName::source = source;

Есть метод который используя данный член класса должен выводить сурфейс на экран.
Например такой:
Код

void ClassName::show() {
    surface.apply_surface( img_x, img_y, source );    
}


Однако сурфейс не выводится.
Если же передавать сурфейс в метод вывода(в примере это show), там его присвоить члену и попробовать вывести, то сурфейс выведиться на экран
Например так:
Код

void StringInput::show( SDL_Surface* s ) {
    source = s;
    surface.apply_surface( img_x, img_y, source );
}


Вопрос:
Подскажите в чём может быть причина?

Если требуется больше информации, спрашивайте.

С нетерпением жду ответа.
Заранее спасибо.

Автор: boostcoder 2.8.2012, 22:35
Цитата(KPEHDEL @  2.8.2012,  22:13 Найти цитируемый пост)
SDL_EnableUNICODE( SDL_ENABLE ); - если вызывать в конструкторе, то после второго вызова для второго объекта

эта функция не имеет ассоциации с объектом. ее бессмысленно вызывать дважды, разве чтоб: SDL_EnableUNICODE(0/1)

Цитата(KPEHDEL @  2.8.2012,  22:13 Найти цитируемый пост)
2.
Информация:
Есть объекты класса с конструктором принмающим параметры.
Требуется объявить один объект, для которого параметры не нужны. По причине, что через этот объект нужно вызывать просто какой-то метод в методе другого класса.
Пример:
Где-то вызывается Button.Header_Menu();
А в Header_Menu() вызываются все остальные "нормальные" объекты с параметрами конструктора.
Поэтому, объекту проводнику параметры не нужны.

Вопрос:
В таком случаи делать Button Button( NULL, NULL, NULL, NULL, NULL, NULL, NULL );
или есть другие способы?

не понял о чем речь...

Цитата(KPEHDEL @  2.8.2012,  22:13 Найти цитируемый пост)
Есть метод который используя данный член класса должен выводить сурфейс на экран.
Например такой:
void ClassName::show() {
    surface.apply_surface( img_x, img_y, source );    
}

Однако сурфейс не выводиться.

покажи конструктор.

Автор: bsa 2.8.2012, 22:49
Цитата(KPEHDEL @  2.8.2012,  23:13 Найти цитируемый пост)
Выбор мой пал всё же не на boost, а на старый добрый Указатель на член. Подходит под задачу и подключать ничего не надо.
я тебе очень сочувствую. ты собрался использовать тот самый костыль, которой существует в языке по ошибке (не додумали разработчики). В итоге, использовать его будет крайне проблематично. Уж поверь. Особенно, когда появится разнообразие классов.

Автор: KPEHDEL 2.8.2012, 23:11
Цитата

эта функция не имеет ассоциации с объектом. ее бессмысленно вызывать дважды, разве чтоб: SDL_EnableUNICODE(0/1)

Я о том и писал, в общем я понял, что использовать метод init() и конструктор вместе это нормально...

Цитата

не понял о чем речь...

Речь о том, нормально ли так объявлять объекты, которые, в отличии от других объектов этого же класса не должны иметь параметров.

Цитата

покажи конструктор.

Код

StringInput::StringInput( int x_img, int y_img, int x_text, int y_text, int w_text, int h_text, SDL_Surface* source ) {
    //Координаты текста в поле ввода.
    text_box.x = x_text;
    text_box.y = y_text;
    text_box.w = w_text;
    text_box.h = h_text;
    
    //Координаты изображения поля ввода.
    StringInput::img_x = x_img;
    StringInput::img_y = y_img;
    StringInput::source = source;
                                        
    //Initialize the string
    str = "";

    //Free text surface
    SDL_FreeSurface( input_text );
}



bsa, пока ничего серьёздного, позже думаю обстоятельства сами заставят перейти на boost::signals, хотя наверное лучше сразу...

Автор: boostcoder 2.8.2012, 23:41
Цитата(KPEHDEL @  2.8.2012,  23:11 Найти цитируемый пост)
StringInput::source = source;

после этой строки добавь:
Код

std::cout << StringInput::source << std::endl;

и в методе StringInput::show() тоже добавь вывод:
Код

std::cout << StringInput::source << std::endl;

ну и сравни значение полученное в конструкторе с тем, что получаешь в StringInput::show().

Цитата(KPEHDEL @  2.8.2012,  23:11 Найти цитируемый пост)
нормально ли так объявлять объекты

показывай кодом. все равно не понимаю.

Автор: Result 3.8.2012, 08:04
Цитата(KPEHDEL @ 2.8.2012,  22:13)
2.
Информация:
Есть объекты класса с конструктором принмающим параметры.
Требуется объявить один объект, для которого параметры не нужны. По причине, что через этот объект нужно вызывать просто какой-то метод в методе другого класса.
Пример:
Где-то вызывается Button.Header_Menu();
А в Header_Menu() вызываются все остальные "нормальные" объекты с параметрами конструктора.
Поэтому, объекту проводнику параметры не нужны.

Вопрос:
В таком случаи делать Button Button( NULL, NULL, NULL, NULL, NULL, NULL, NULL );
или есть другие способы?

Можно добавить еще один конструктор т.н. конструктор по умолчанию :
Код

//*.h
struct Button{
    Button();
    Button(T* arg1, Y* arg2);
    
};

//*.cpp
Button::Button(T* arg1, Y* arg2){/**/}

Button::Button(){/**/}


Автор: borisbn 3.8.2012, 08:27
Цитата(KPEHDEL @  2.8.2012,  22:13 Найти цитируемый пост)
Я использую конструктор/деструктор, но также когда требуется создаю метод init() в котором вызывается, то, что должно быть вызвано единажды, а не для каждого объекта.

зачем тогда init - метод этого класса ? Если нужно вызвать что-то один раз сделай так
Код
MyClass::MyClass()
{
    static bool /*или что она там возвращает*/ res = SDL_EnableUNICODE( SDL_ENABLE );
}


Автор: bsa 3.8.2012, 12:35
Цитата(KPEHDEL @  3.8.2012,  00:11 Найти цитируемый пост)
позже думаю обстоятельства сами заставят перейти на boost::signals, хотя наверное лучше сразу...
поверь мне, когда доходит до "обстоятельства сами заставят", то будешь биться головой об стенку, так как поменять сотни-тысячи строк в нескольких десятках файлов - удовольствие не для каждого.

Автор: KPEHDEL 8.8.2012, 14:17
boostcoder, была глупая ошибка, конструктор вызывался раньше майна, исправил.

Цитата

Можно добавить еще один конструктор т.н. конструктор по умолчанию :


Спасибо, так и сделаю.

Цитата

зачем тогда init - метод этого класса ? Если нужно вызвать что-то один раз сделай так

Спасибо, так и сделаю.

Всем спасибо, пока больше вопросов нет.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)