Передача функции-члена класса параметру вне класса

Как передать функцию-член класса в качестве параметра другой функции-члену другого класса?

class theSecondClass
{
    public:
    void theFunctionReceiver(void (theFirstClass::*Function)(void));
    {
         // This part is wrong. "Operand of * must be a pointer"
         (*Function)(); 
    }
}

class theFirstClass
{
    public:
    theSecondClass * SecondClassInstance;
    void theFunctiontoPass(void)
    {
        printf("It worked \n");
        return;
    }
    void theFunctiontoCall(void)
    {
        SecondClassInstance->theFunctionReceiver(theFunctiontoPass);
    }
};

Примите предположение, что theSecondClass а также theFirstClass оба уже сделаны. Я зову theFirstClass->theFunctiontoCall() откуда-то

Я не понимаю Когда я передаю это, разве это не передается как указатель?

Я посмотрел на несколько похожих тем вокруг, но я не понимаю их полностью. Я использую VS 2013, базовый компилятор.

1 ответ

Решение

Когда вы пишете это заявление:

SecondClassInstance->theFunctionReceiver(theFunctiontoPass);

Вероятно, вы имели в виду:

SecondClassInstance->theFunctionReceiver(&theFunctiontoPass);

Что должно дать вам предупреждение компилятора о том, что это неквалифицированная ссылка на член, что указывает на то, что вы на самом деле пишете:

SecondClassInstance->theFunctionReceiver(&theFirstClass::theFunctiontoPass);

Вы получаете указатель на функцию-член в определении класса. "Это" не подразумевается и не входит в пакет. Единственный способ вызвать его без экземпляра класса - это если он статический. (В этом случае он не будет проверять тип как функцию-член... это будет просто указатель на обычную функцию.)

Если я собираюсь передать ссылку на мой класс, зачем мне вообще передавать эту функцию? Не могу ли я просто вызвать его, в случае ссылки, ButtonObj->Buttonfunc();

Единственная причина, по которой вы будете использовать указатели на функции-члены, состоит в том, чтобы получить некоторую абстракцию, где один фрагмент кода может вызывать функцию-член, которой не нужно явно называть имя. Если ты в порядке с theSecondClass::theFunctionReceiver зная имя theFirstClass::theFunctionToPass и личность theFirstClass... тогда обязательно просто передайте ссылку на экземпляр theFirstClass и явно вызовите метод.

Вы можете захотеть ситуацию, когда theSecondClass собирается вызвать любую из нескольких функций-членов на theFirstClass с соответствующими сигнатурами... он просто не хочет жестко кодировать какую. В этом случае можно передать пару ссылок на класс и функцию-член. Вы, кажется, подозреваете, что это не слишком часто бывает полезным, и вы были бы правы. Каждый год мне приходится возвращаться и искать синтаксис для вызова указателей на члены в классе, потому что он почти никогда не появляется, за исключением вопросов Stackru:

Как позвонить через указатель на функцию-член?

Скорее всего, что вы хотите (и что на самом деле хотят люди, задающие эти вопросы), так это разделить проблемы так, чтобы theSecondClass имеет крюк для выполнения чего-либо, но не должен знать о theFirstClass совсем. Посмотрите на lambdas, std:: function и std::bind, чтобы найти обобщенные решения, с которыми вы, возможно, сможете поэкспериментировать.

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

#include <iostream>
#include <functional>

class theSecondClass {
public:
    void theFunctionReceiver(std::function<void()> const & Function) {
         Function(); 
    }
};

class theFirstClass {
private:
    theSecondClass * SecondClassInstance;
public:
    void theFunctiontoPass() {
        std::cout << "It worked\n";
    }
    void theFirstClass::theFunctiontoCall() {
        SecondClassInstance->theFunctionReceiver(
            [this]() {theFunctiontoPass();}
        );
    }
};

int main() {
    theFirstClass tfc;
    tfc.theFunctiontoCall();
}

Обратите внимание, что это C++11, который я предлагаю использовать, если вы еще этого не сделали. Однако в C++98 существуют менее удобные обозначения и механизмы.

Это исправляет проблемы с вашим кодом, которые выходят за рамки упомянутой вами проблемы. Пожалуйста, просмотрите написание минимального, полного, проверяемого примера. Должна быть возможность вставить предоставленный код в компилятор и увидеть только ту ошибку, которую вы хотите обсудить.

  • Это добавляет точки с запятой после окончания определений классов
  • Это удаляет точку с запятой после объявления метода, когда вы предоставляете тела в классе
  • Вы нуждались в различных предварительных определениях, чтобы заставить его работать так, как оно было у вас, это не требует их
  • Когда функция не принимает параметров, ее обычно определяют как void foo() не void foo(void), return; последняя строка функции, не возвращающей значения, также является излишней.
  • Избегайте написания нового кода на C++ с использованием printf, изучайте iostreams
  • Переменные члены смещены на частную или защищенную.
  • В образцах кода Stackru старайтесь, чтобы они были короткими и не нуждались в полосах прокрутки; лучше не давать открывающим скобкам собственную линию (большую часть времени)

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

Другие вопросы по тегам