Как безопасно передать указатель this в другую функцию? - PullRequest
1 голос
/ 17 января 2020

Я определил класс, который содержит ссылку на список функций c, которые определены вне класса. Каждая из этих функций принимает в качестве аргумента указатель на этот конкретный экземпляр класса.

Я передаю указатель this на функции. Но это не кажется мне правильным.

Есть ли лучший способ?

Код, приведенный ниже, является упрощенной версией:

#include <iostream>
#include <map>

class A
{
    public:
       typedef void (*action_func)(A*);
       typedef std::map<int, action_func> func_map;

       A(func_map the_map)
          :m_map(the_map)
       {}

       void execute_action(int action_id)
       {
          auto it = m_map.find(action_id);
          if(it != m_map.end())
          {
             auto cmd = it->second;
             cmd(this);
          }
       }

    private:
       func_map&  m_map;
};


static void function_1(A* ptrToA)
{
    std::cout << "This is function_1\n";
}

static void function_2(A* ptrToA)
{
    std::cout << "This is function_2\n";
}


static func_map functions =
{
    {1, function_1},
    {2, function_2}
};


int main()
{
   A obj(functions);
   obj.execute_action(1);
   obj.execute_action(2);
   return 0;
}

Вывод вышеприведенного выглядит так:

This is function_1
This is function_2

1 Ответ

2 голосов
/ 18 января 2020

При хранении ссылок и указателей на вещи вне класса важно рассуждать об их продолжительности жизни. Как правило, ссылка на объект должна переживать вещь, которая на нее ссылается.

В вашем конкретном случае static func_map functions; имеет stati c длительность хранения , то есть оно создано до main() начинается и уничтожается после main() окончания.

Таким образом, вы можете безопасно использовать его внутри A obj, который находится в пределах main():

int main()
{
   A obj(functions);
   . . .
}

Однако конструктор A не просто хранит его - он хранит ссылку на свою временную копию:

   A(func_map the_map)
      :m_map(the_map)
   {}
   func_map&  m_map;

Что еще хуже, временная копия сохраняется только до конца полного выражения, то есть до конец A obj(functions);. Поэтому, если вы будете использовать его после этого, у вас будет доступ к висячей ссылке (неопределенное поведение).

Чтобы исправить это, измените его на передачу по ссылке:

   A(func_map& the_map)
      :m_map(the_map)
   {}
   func_map&  m_map;

Теперь нет проблема.

Я передаю указатель this на функции. Но мне это не кажется правильным.

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

Независимо от того, является ли это "правильным" с точки зрения дизайна, невозможно сказать из предоставленного примера. Могут быть и лучшие решения, но есть также шаблоны проектирования (например, шаблон стратегии), основанные на передаче ссылки на себя. Так что, в конце концов, это выбор дизайна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...