доступ к скрытому указателю «это» - PullRequest
1 голос
/ 14 апреля 2010

У меня есть архитектура GUI, в которой элементы запускают события, например, так:

guiManager->fireEvent(BUTTON_CLICKED, this);

Каждое отдельное событие передает «this» как вызывающее событие. Никогда не бывает времени, когда я не хочу передать 'this', и, кроме того, указатель , кроме для слова "this", никогда не должен передаваться.

Это подводит меня к проблеме: как я могу утверждать, что fireEvent никогда не дается указатель, отличный от 'this', и как я могу упростить (и гомогенизировать) вызовы fireEvent до:

guiManager->fireEvent(BUTTON_CLICKED);

В этот момент мне вспоминается довольно распространенная ошибка компилятора, когда вы пишете что-то вроде этого:

class A {
public:
    void foo() {}
};

class B {
    void oops() { const A* a = new A; a->foo(); }
};

int main() {
    return 0;
}

Компиляция этого даст вам

В функции-члене Oid void B :: oops () ’: ошибка: прохождение «Const A» как «this» аргумент «void» A :: foo () ’отбрасывает квалификаторы

потому что функции-члены передают 'this' в качестве скрытого параметра.

"Ага!" Я говорю. Это (без каламбура) именно то, что я хочу. Если бы я мог как-то получить доступ к скрытому указателю «this», это решило бы обе проблемы, о которых я упоминал ранее. Проблема в том, насколько я знаю, что вы не можете (не так ли?), И если бы вы могли, были бы крики "но это нарушит инкапсуляцию!" За исключением того, что я уже пропускаю «это» каждый раз, так что еще может сломаться.

Итак, - это есть ли способ получить доступ к скрытому «этому», и если нет, то есть ли какие-то идиомы или альтернативные подходы, которые более элегантны, чем каждый раз передавать «это»?

Ответы [ 4 ]

6 голосов
/ 14 апреля 2010

Вы можете определить

void Element::fireEvent(EVENT e) {
   guiManager->fireEvent(e, this);
}

чтобы каждый раз немного сэкономить. Вам нужно будет позвонить с this на некоторой точке, поскольку guiManager нужно знать, какой Element назвал fireEvent.

1 голос
/ 14 апреля 2010

Это зависит от сферы действия guiManager.

Вы можете переписать это:

class MyClass
{
public:

private:
  void FireEvent(GuiManager* guiManager, EventType e)
  {
    assert(guiManager);
    guiManager->FireEvent(e, this);
  }
};

Есть ли причина для передачи this (типа MyClass*) вместо *this (типа MyClass&)? Если вы передаете указатель, это означает, что вы ожидаете, что объект, возможно, будет NULL ... и this никогда не будет NULL (исключая опасные эксплойты неопределенного поведения)

1 голос
/ 14 апреля 2010

Я не уверен, что понимаю вторую половину вашего вопроса (и здесь одно правило является хорошим правилом), но в случае:

guiManager->fireEvent(BUTTON_CLICKED, this);

Вы могли бы дать классу этот код, вызываемый из конструктора, который принимает guiManager в качестве параметра - назовите его mGM. А затем предоставьте функцию-член, которая выглядит следующим образом:

void FireEvent( EventType e ) {
   mGM->fireEvent( e, this );
}

который вы называете:

FireEvent( BUTTON_CLICKED );
0 голосов
/ 14 апреля 2010

Другой вариант - создать макрос #define, чтобы компилятор добавил для вас параметр «this». Это будет выглядеть примерно так:

#define FIREEVENT(gm,e) gm->fireEvent(e, this)
FIREEVENT(guiManager, BUTTON_CLICKED); //compiles as: guiManager->fireEvent(BUTTON_CLICKED);

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

...