Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Сравнить два указателя


Автор: borisbn 9.10.2012, 08:57
Здравствуйте.
Имеется два интерфейса
Код
class A {
public:
   virtual void fooA() = 0;
   virtual ~A() {}
};

class B {
public:
   virtual void fooB() = 0;
   virtual ~B() {}
};

и такой класс
Код
class MainSystem
{
public:
   void installA( A * a ) { m_a = a; }
   void installB( B * b ) { m_b = b; }

   MainSystem() : m_a( 0 ), m_b( 0 ) {}
   ~MainSystem() {
      if ( m_a == m_b ) { // <--- Не работает. Приведение к void* - тоже
         delete m_a;
      }
      else {
         delete m_a;
         delete m_b;
      }
   }
private:
   A * m_a;
   B * m_b;
};

Т.е. я хочу сделать следующее: если пользователь класса MainSystem наследовался от обоих интерфейсов и передал мне один и тот же указатель, то удалять его нужно один раз. Если он сделал два независимых наследника, то удалять нужно обоих.
Вопрос: как мне узнать какой вариант выбрал пользователь ?
P.S. Я пробовал наследовать оба интерфейса он некоего Base (пустого) и сравнивать не A* и B*, а Base* и Base*, но тоже безрезультатно.
Код
class Base {};
class A : public Base { ... };
class B : public Base { ... };
   ~MainSystem() {
      if ( (Base*)m_a == (Base*)m_b ) { // <--- Не работает.
...

http://liveworkspace.org/code/e9bc0e79aa98419521be0bfdb490cee3

Автор: Alexeis 9.10.2012, 09:24
Цитата(borisbn @  9.10.2012,  09:57 Найти цитируемый пост)
Т.е. я хочу сделать следующее: если пользователь класса MainSystem наследовался от обоих интерфейсов и передал мне один и тот же указатель, то удалять его нужно один раз. Если он сделал два независимых наследника, то удалять нужно обоих.

  Обычно делается подсчет ссылок. 2 раза присвоил, то счетчик ссылок +2. Вместо удаления уменьшаешь счетчик ссылок. Когда он станет равен нулю, то функция релиз удаляет объект сама.

Автор: borisbn 9.10.2012, 09:38
Для того, чтобы счётчик увеличился до 2-х нужно, чтобы этот счётчик был единым для обоих указателей (A* и B*). Если добавить счётчик в Base, то будут создано 2 счётчика... В общем, не пойму как реализовать подсчёт ссылок для данного случая.
http://liveworkspace.org/code/d06d40d334085eb9b8bacf146f26a30b

Автор: baldina 9.10.2012, 10:02
delete 0 не вызывает неприятностей, поэтому удаляйте оба, только после удаления каждого присваивайте 0

Добавлено через 2 минуты и 49 секунд
но вообще-то тут где-то логическая неувязка, несоответствие RAII: кто владелец указателей?
наконец, можно умные указатели использовать

Автор: mabrarov 9.10.2012, 10:13
Вообще, правильные варианты уже предложили.
Но если очень хочется, то можно через http://liveworkspace.org/code/286c0a4a56c85823b1e7a4d63a1f16c6

Автор: Alexeis 9.10.2012, 10:35
Цитата(borisbn @  9.10.2012,  10:38 Найти цитируемый пост)
 Если добавить счётчик в Base, то будут создано 2 счётчика... В общем, не пойму как реализовать подсчёт ссылок для данного случая.

  А зачем реализовывать счетчик в интерфейсах? Реализуй его в конечном классе. Ты же написал в начале поста, что у тебя интерфейс. Интерфейс не предполагает использование полей данных внутри себя. Наследуй интерфейс от IUnknown и реализуй в конечном классе для обоих интерфейсов. 

Автор: borisbn 9.10.2012, 10:38
baldina, у меня же два указателя... соотв. присваивая 0 одному другой-то не поменяется...

mabrarov, спасибо! Именно то, что нужно.

Век живи, век учись...
/me пошёл читать про виртуальное наследование

Автор: Alexeis 9.10.2012, 10:39
Я для наследования 2х интерфейсов делал вот такой класс заготовочку. 
Код

template<typename tInterface1, typename tInterface2,
         typename tquery1 = tInterface1, typename tquery2 = tInterface2> 
class C2InterfacedObject : public tInterface1, public tInterface2
{
protected:
   volatile long  RefCount;

public:
    C2InterfacedObject( void ){RefCount = 0;};
    virtual ~C2InterfacedObject( void ){};

//IUnknown
    virtual ULONG   __stdcall AddRef( void ){return InterlockedIncrement(&RefCount);};
    virtual ULONG   __stdcall Release( void )
    {
        if (InterlockedDecrement(&RefCount) == 0)
        {
            delete this;
            return 0;
        }
        else
          return RefCount;
    }

    virtual HRESULT __stdcall QueryInterface(const _GUID &IID, void* *Obj)
    {
        if (Obj == NULL) return E_POINTER;

        if      (IsEqualGUID(__uuidof(tInterface1), IID) ||
            IsEqualGUID(__uuidof(tquery1),     IID))
            *Obj = static_cast<tInterface1*>(this);

        else if (IsEqualGUID(__uuidof(tInterface2), IID) ||
            IsEqualGUID(__uuidof(tquery2),     IID) ||
            IsEqualGUID(__uuidof(IUnknown),    IID))
            *Obj = static_cast<tInterface2*>(this);
        else
            *Obj = NULL;

        if (*Obj) static_cast<IUnknown*>(*Obj)->AddRef();
        return (*Obj) ? S_OK : E_NOINTERFACE;
    }
};


Наследуюсь от него и имею общий счетчик ссылок.

Автор: borisbn 9.10.2012, 11:36
Alexeis, спасибо, понятно. Но мне не очень подходит, т.к. я хочу минимизировать требования к пользователям моего класса MainSystem. Т.е. хочешь наследоваться только от одного интерфейса - пожалуйста, хочешь от двух - без проблем... как-то так...

Автор: Alexeis 9.10.2012, 12:03
Цитата(borisbn @  9.10.2012,  12:36 Найти цитируемый пост)
 Т.е. хочешь наследоваться только от одного интерфейса - пожалуйста, хочешь от двух - без проблем... как-то так... 

  Так в чем вопрос? Это пример как делается один счетчик на 2 интерфейса. В общем-то в твоем случае QueryInterface не нужен, просто если наследоваться от IUnknown, то можно использовать умный указатель ComPtr<> . Кстати, в бусте есть умный указатель с внешним счетчиком ссылок, тогда можно совсем не связываться с комом boost::intrusive_ptr

Автор: baldina 9.10.2012, 14:30
Цитата(borisbn @  9.10.2012,  10:38 Найти цитируемый пост)
baldina, у меня же два указателя

 smile верно

Автор: maxim1000 9.10.2012, 18:48
Код

dynamic_cast<void*>(a)==dynamic_cast<void*>(b)

Автор: borisbn 9.10.2012, 19:42
Цитата(maxim1000 @  9.10.2012,  18:48 Найти цитируемый пост)
dynamic_cast<void*>(a)==dynamic_cast<void*>(b)

Цитата(borisbn @  9.10.2012,  10:38 Найти цитируемый пост)
Век живи, век учись...

хммммммм. 
сначала увидел это сообщение с мобильного и проверить не было возможности (привет boostcoder'у))) и думал ответить, что это - ерунда, но потом добрался до "большого" компа - проверил - работает.
интересно, а что делает dynamic_cast для void* ????

maxim1000, не подскажешь, ты до этого методом тыка дошёл или прочитал где ? Если второе - дай ссыль, плиз.
И ещё вопрос - это случайно в gcc так получается или это чётко по стандарту ?

Спасибо.

Добавлено через 2 минуты и 29 секунд
Кстати, первое, что я попробовал - это
Код
(void*)a == (void*)b

и оно не отработало. reinterpret_cast и static_cast - тоже. 

Автор: Alexeis 9.10.2012, 20:10
Цитата(borisbn @  9.10.2012,  20:42 Найти цитируемый пост)
И ещё вопрос - это случайно в gcc так получается или это чётко по стандарту ?

Спасибо.

Цитата(http://msdn.microsoft.com/en-us/library/cby9kycs.aspx)

If type-id is void*, a run-time check is made to determine the actual type of expression. The result is a pointer to the complete object pointed to by expression.

Автор: maxim1000 9.10.2012, 22:48
Цитата(borisbn @  9.10.2012,  19:42 Найти цитируемый пост)
maxim1000, не подскажешь, ты до этого методом тыка дошёл или прочитал где ?

когда-то обсуждали вопросы для собеседований, вот со мной и поделились smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)