2015-07-06 5 views
2

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

Для иллюстрации:

struct Button { 
    Button(void (*functionPtr)()) { 
     // Store the function pointer to be called later 
    } 
}; 

struct SomeClass { 
    void LoadFile(); 

    SomeClass() { 
     Button* temp1 = new Button(&LoadFile); // ??? 
    } 
}; 

struct AnotherClass { 
    void SaveFile(); 

    SomeClass() { 
     Button* temp2 = new Button(&SaveFile); // ??? 
    } 
}; 

Как я могу сделать эту работу?

+1

Как насчет хранения 'std :: function ' члена, а затем передачи lambdas, которые выполняют захват класса для вас? –

+0

Надеюсь, вы понимаете, что вам нужно «это» так или иначе, будь он похоронен в связанной оболочке или снабжен указателем на функцию (который в настоящее время даже не является указателем на функцию-член). Я бы начал с этого. – WhozCraig

+0

Возможно, вы захотите проверить ['boost :: сигналы'] (http://www.boost.org/doc/libs/1_58_0/doc/html/signals.html), хотя, как отмечает @BaummitAugen,' ' std :: function'' + lambdas теперь отлично подходят для этого. –

ответ

3

Указатель на функцию и функция-указатель на элемент, несмотря на кажущееся похожим, на самом деле совершенно разные звери.

void (*functionPtr)() - это указатель на функцию, которая не принимает аргументов и возвращает void. &AnotherClass::SaveFile является указателем на член функция AnotherClass ... его тип void (AnotherClass::*)(). Обратите внимание, что имя класса является частью типа, поэтому вы не можете просто сохранить функцию-указатель-член для произвольного класса. Кроме того, для вызова функции «указатель-член» вам нужен указатель на экземпляр - вам нужно его каким-то образом сохранить, но у них тоже будут разные типы!

Что вы можете сделать вместо этого в C++ 11 является использование типа стирания:

std::function<void()> callback; 

и присвоить произвольное вызываемым к нему:

template <typename F> 
Button(F cb) 
: callback(cb) 
{ } 

И тогда вы могли бы создать кнопку с помощью std::bind:

Button* temp1 = new Button(std::bind(&OtherClass::LoadFile, this)); 
Button* temp2 = new Button(std::bind(&AnotherClass::SaveFile, this)); 

Теперь temp1->callback() будет на самом деле назвать LoadFile() на примере OtherClass, что он был построен с. Это то, что вы хотели.И мы по-прежнему можно использовать бесплатные функции:

void whatever(); 
Button* temp3 = new Button(whatever); 

Вставьте обычные предостережения об использовании необработанных указателей и new и предпочитая unique_ptr.

+0

Я видел что-то похожее на это в другом месте, но я до сих пор этого не понимаю. Возвращает переменную-член в button.h. и что именно происходит при создании temp1? – GravenFear

+0

@GravenFear Да, и [здесь] (http://en.cppreference.com/w/cpp/utility/functional/function) являются документами для 'std :: function' – Barry

+0

Когда я использую bind без '&' он жалуется, что в функции, которую я передаю, отсутствует список аргументов, но когда я добавляю «&», он дает мне 5-строчную ошибку «неразрешенный внешний символ» в окне вывода (я использую Visual Studio). Есть идеи? – GravenFear

0

Вы не можете иметь указатель на функцию-член, потому что это бессмысленно без скрытого указателя this, на который вызывается объект.

Существует несколько распространенных решений проблемы, которую вы пытаетесь решить.

C путь:

магазин void* в дополнение к указателю функции. Затем передайте void* внутри обратного вызова. Это менее «безопасно», но часто встречается в С.

OOP способ # 1:

Подкласс Button и есть виртуальная функция в базовом классе, скажем onPress, которые могут быть реализованы в подклассах.

OOP способ # 2:

имеют интерфейс обратного вызова независимо от Button, что пользовательские классы реализации.

EDIT: Или используйте лямбда, как указано в комментариях. Я все еще переосмысливаю C++ по-своему.

Смежные вопросы