Получить (указатель на) вызывающий объект - PullRequest
2 голосов
/ 25 ноября 2010

У меня есть объект - класс планировщика-. Этот класс планировщика получает указатели на функции-члены, время и указатель на объект, который создал планировщик.
Это означает, что я мог бы сделать что-то еще: (pObject->*h.function)(*h.param); Где pObject - указатель на исходный объект, h класс, который содержит function + указатель void parameter, поэтому я могу передавать аргументы исходной функции. Когда мне нравится инициализировать этот объект, у меня есть конструктор explicit Scheduler(pObjType o); (где pObjType - параметр шаблона).

Когда я создаю объект, который должен иметь этот сигнал, я печатаю:

struct A {
    typedef void (A::*A_FN2)(void*);
    typedef Scheduler<A*,A_FN2> AlarmType;
    A(int _x) : alarm(NULL)
    {
        alarm.SetObject(this);
    }
    AlarmType alarm

Однако этот тип тревоги накладывает довольно большое ограничение на объект: если я забуду добавить конструктор копирования (к A ), класс получит неопределенное поведение. планировщик будет продолжать указывать на исходный объект, и этот исходный объект может выходить за рамки или, что еще хуже, может и не быть.

Есть ли метод, который, когда я копирую свою тревогу (по умолчанию, так в конструкторе копирования планировщика), я могу получить вызывающий объект (и указатель на это?)?
Или, если это невозможно, возможно ли выдать ошибку (компиляции), если я забуду реализовать конструктор копирования для моей структуры? - А попытаться скопировать эту структуру куда-нибудь?

Ответы [ 3 ]

1 голос
/ 25 ноября 2010

На мой взгляд, у вас есть возможность улучшить свой дизайн, что поможет вам избавиться от беспокойства.

  1. Обычно плохая идея передавать указатели на функции-члены.Лучше, чтобы ваши структуры наследовали от абстрактного базового класса, делая функции, которые вы хотите настроить, абстрактными виртуальными.
  2. Если вам не нужно копировать, лучше запретить его в базовом классе.Либо сделав конструктор и оператор копирования неопределенными и закрытыми, либо наследовав boost::NonCopyable.
0 голосов
/ 26 ноября 2010

Самый простой способ предотвратить конкретную проблему, о которой вы спрашиваете, - сделать Scheduler некопируемым (например, с boost::noncopyable). Это означает, что любой клиентский класс, включающий элемент значения типа Scheduler, не сможет быть скопирован. Мы надеемся, что это даст подсказку программисту проверить документы и выяснить семантику копирования Scheduler (т.е. создать новый Scheduler для каждого нового A), но кто-то может ошибиться если они решают проблему, просто удерживая указатель Scheduler. Псевдоним указателя создает ту же проблему, что и конструирование по умолчанию при копировании экземпляра Scheduler, который содержит указатель.

Каждый раз, когда у вас есть необработанные указатели, вы должны иметь политику в отношении времени жизни объекта. Вы хотите, чтобы время жизни любого класса A было не меньше, чем у соответствующего экземпляра Scheduler, и, как я вижу, есть три способа обеспечить это:

  • Использовать композицию - в этом случае невозможно, потому что A содержит Scheduler, поэтому Scheduler не может содержать A
  • Использовать наследование, например в форме любопытно повторяющегося шаблона (CRTP)
  • Иметь глобальную политику по обеспечению продолжительности жизни A экземпляров, например требование, чтобы они всегда удерживались умным указателем, или чтобы их очистка была обязанностью некоторого класса, который также знает, что нужно очистить Scheduler s, которые зависят от них

CRTP может работать так:

#include <iostream>

using namespace std;

template<typename T>
struct Scheduler {
    typedef void (T::* MemFuncPtr)(void);

Scheduler(MemFuncPtr action) : 
    action(action)
    {
    }

  private:
    void doAction()
    {
        this->*action();
    }

    MemFuncPtr action;
};

struct Alarm : private Scheduler<Alarm> {
    Alarm() : Scheduler<Alarm>(&Alarm::doStuff)
    {
    }

    void doStuff()
    {
        cout << "Doing stuff" << endl;
    }
};

Обратите внимание, что частное наследование гарантирует, что клиенты класса Alarm не смогут рассматривать его как необработанный Scheduler.

0 голосов
/ 25 ноября 2010

Если вам нужна какая-либо семантика построения автоматического копирования, вам нужно перейти к CRTP - никакой другой шаблон не предоставляет указатель на объект-владелец.

Другое дело, что вы действительно должны использовать boost :: / std :: function <>, они гораздо более общие, и вам это понадобится, если вы хотите использовать функции Lua.

...