2016-05-27 1 views
1

У меня есть два указателя указателей на классы A и B и пытаюсь сделать метод, который удаляет объект из этих векторов. Этот объект может быть или не быть подклассом A и/или B и не должен быть элементом любого из векторов. Я попытался следующий код:C++ сравнения указателей с использованием множественного наследства с неизвестным базовым классом

void removeObject(void *object) { 
    A *pa = static_cast<A*>(object); 
    std::vector<A*>::iterator aPos = std::find(as.begin(), as.end(), pa); 
    if (aPos != as.end()) 
     as.erase(aPos); 
    B *pb = static_cast<B*>(object); 
    std::vector<B*>::iterator bPos = std::find(bs.begin(), bs.end(), pb); 
    if (bPos != bs.end()) 
     bs.erase(bPos); 
} 

но указатели имеют различные значения, так std::find не работает (или, скорее, он работает иногда). После прочтения Multiple inheritance pointer comparison и C++ pointer multi-inheritance fun Я понимаю, почему, но в обоих случаях предлагаемым решением было указать указатель на *subclass. Мои вопросы:

  1. Как проверить, ссылаются ли два указателя на один и тот же объект , не зная его типа?
  2. Есть ли более элегантное решение моей проблемы?

Я новичок в C++ поэтому, пожалуйста, простите меня, если я полностью понял что-то ...

+0

Я не верю, что на самом языке есть решение. Ваше решение, вероятно, будет включать соглашение ... например, все объекты, которые вы собираетесь сравнить таким образом, наследуют от класса (интерфейса), который предоставляет единственную виртуальную функцию, которая возвращает void *, которая реализуется посредством 'return static_cast (this) ', а затем вы сравниваете указатели, которые вы возвращаете с этого вызова. Если вы программист Windows - это похоже на то, как вы всегда сравниваете два COM-объекта, сначала создавая QueryInterface для IUnknown, а затем сравнивая эти два указателя IUnknown. – davidbak

+0

Пожалуйста, объясните свою иерархию классов. Кажется, что и A, и B необходимо связать через некоторый общий базовый класс, который будет иметь информацию типа в форме, например. ENUM. В этом случае вы можете применить void * к этому базовому классу и найти базовый тип, прочитав перечисление, которое должно было быть установлено при создании экземпляра класса.Может быть какое-то другое сложное решение, но я не могу придумать его, особенно с множественным наследованием, он становится более сложным – Arunmu

ответ

2

Если ваши классы являются полиморфными (по крайней мере, одну виртуальную функцию), то dynamic_cast<void *>() возвращает указатель на начало самого производного объекта, поэтому он всегда будет возвращать один и тот же ответ для одного и того же объекта, даже если вы начинаете с указателей на несвязанные базовые классы.

Но если вы уже конвертировали указатель, вы должны были void *, это слишком поздно для этого. Возможно, вам будет лучше просто сохранить указатели на некоторый общий базовый класс.

+1

Я не знал этого (о 'dynamic_cast ', спасибо. – davidbak

0

void* - это старая концепция C на C++, вы должны знать, с каким типом вы работаете, потому что это основа программирования шаблонов. С этой целью позволяет изменить функцию подписи:

template <typename T> 
void removeObject(T* object); 

Отсюда мы можем использовать is_base_of, чтобы определить, является ли тип является основой элементов каждого vector «s value_type:

if(is_base_of_v<T, decltype(as)::value_type>) { 
    auto aPos = find(cbegin(as), cend(as), *static_cast<A*>(object)); 

    if(aPos != cend(as)) as.erase(aPos); 
} 

if(is_base_of_v<T, decltype(bs)::value_type>) { 
    auto bPos = find(cbegin(bs), cend(bs), *static_cast<B*>(object)); 

    if(bPos != cend(bs)) bs.erase(bPos); 
} 

Очевидно, static_cast будет полностью незаконным, если object был указателем на фактический базовый класс. Я предполагаю, что хотя object является указателем на базовый класс, он действительно имеет тип static_cast, иначе нам нужно вернуться к чертежной доске.