LINUX.ORG.RU

Как узнать тип указателя без typeid и dynamic_cast

 


0

2

Требуется реализовать функцию, которая принимает на вход два указателя на базовый класс Expression, и возвращает true, если оба указателя указывают на самом деле на объекты одного и того же класса, и false в противном случае.

C typeid все делается в одну строку:

bool check_equals(Expression const *left, Expression const *right)
{
    return (typeid(*left) == typeid(*right));
}

А как сделать тоже самое без typeid и dynamic_cast?

★★

Последнее исправление: beastie (всего исправлений: 1)

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

hlebushek ★★
()
Ответ на: комментарий от hlebushek

Ну или просто сделай свой аналог RTTI в этих классах.

hlebushek ★★
()

Через паттерн Визитор.

yoghurt ★★★★★
()

виртуальный метод, возвращающий идентификатор типа (строка или число).

crowbar
()

твои пожелания не совместны.

помань, что Страустроп преследовал(ет), что бы понять почему необходимо сообщить компилятору

qulinxao ★★☆
()

А как сделать тоже самое без typeid и dynamic_cast?

Делаешь шаблонную функцию которая возвращает false и специализацию для Expression const * которая возвращает true.

no-such-file ★★★★★
()

Может подойдет такой вариант:

class Expression
{
  enum Type { type1, type2,.. };
  virtual Type getType() const = 0;
}

andreyu ★★★★★
()

Это все не то, тут надо как то через таблицу виртуальных методов...

Pirr ★★
() автор топика
Ответ на: комментарий от Pirr

Те кто выполнил это задание утверждают, что решение очень простое и чуть ли не в одну строку, а так же что оно не придерживается стандарта.

Pirr ★★
() автор топика
Ответ на: комментарий от Pirr

Если костылить и гогнокодить, то (*reinterpret_cast<void**>(left) == *reinterpret_cast<void**>(right))

mashina ★★★★★
()
Последнее исправление: mashina (всего исправлений: 1)
Ответ на: комментарий от Pirr

Ну, раз непереносимое, то вот тебе вариант, работающий для gcc:

#include <iostream>
#include <memory>

class Expression
{
public:
	virtual ~Expression() {}

	virtual const void* my_type_id() const
	{
		return *(reinterpret_cast<const void* const*>(this));
	}
};

class ConcreteExpressionA : public Expression
{

};

class ConcreteExpressionB : public Expression
{

};

bool expression_type_equal(const Expression* lhs, const Expression* rhs)
{
	return (lhs->my_type_id() == rhs->my_type_id());
}

int main()
{
	std::unique_ptr<Expression> a(new ConcreteExpressionA());
	std::unique_ptr<Expression> b(new ConcreteExpressionA());
	std::unique_ptr<Expression> c(new ConcreteExpressionB());

	std::cout << expression_type_equal(a.get(), b.get()) << "\n"
	          << expression_type_equal(b.get(), c.get()) << "\n"
	          << expression_type_equal(c.get(), c.get()) << "\n";
}

ilammy ★★★
()
Ответ на: комментарий от ilammy

Я не могу переписать class Expression, тут нужен какой то нестандартный ход связанный с таблицами виртуальных методов.

Pirr ★★
() автор топика
Ответ на: комментарий от Pirr

Пойнтер на таблицу лежит непосредственно перед объектом, какие проблемы их достать и сравнить для двух объектов? Если совпадают тип один, не совпадают - разные.

С-но пойнтер на объект именно на пойнтер на таблицу и указывает ЕМНИП (если объект вирт). Т.е. что то вроде

*((*long)p1)==*((*long)p2)

ЗЫ mashina опередил;-)

aiv@aivbook:~/tmp$ cat test2.cpp
#include <iostream>
using namespace std;

class A{
        virtual void f() = 0;
};
class B: public A{
        void f(){}
};
class C: public A{
        void f(){}
};

int main(){
        B b1, b2; C c;
        cout<<*(long*)&b1<<" "<<*(long*)&b2<<" "<<*(long*)&c<<endl;
        return 0;
}
aiv@aivbook:~/tmp$ g++ -o test2 test2.cpp 
aiv@aivbook:~/tmp$ ./test2
4197712 4197712 4197680
AIv ★★★★★
()
Последнее исправление: AIv (всего исправлений: 1)
Ответ на: комментарий от Pirr

Я не могу переписать class Expression, тут нужен какой то нестандартный ход связанный с таблицами виртуальных методов.

Не нужно ничего переписывать, объект имеет вирт. таблицу по твоим начальным условиям.

mashina ★★★★★
()
Ответ на: комментарий от AIv

Похоже это то что нужно!

Можно спросить что дает первая звездочка в *(long*)&b1? Я делал почти так же но не ставил эту первую звездочку и ничего у меня не получалось.

Pirr ★★
() автор топика
Ответ на: комментарий от mashina

Спасибо за помощь, но Ваш ответ я не сразу понял.

Pirr ★★
() автор топика
Ответ на: комментарий от Pirr

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

Вам доступно изменение базового класса и его наследников?

andreyu ★★★★★
()
Ответ на: комментарий от Pirr

Только учти, что это хак, опирающийся на конкретные реализации языка, со всеми вытекающими последствиями

Gvidon ★★★★
()

Ты что-то делаешь не так. Зачем тебе это?

Я бы добавил в класс int typeID = ... и проверял бы. Если уж так надо.

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 2)
Ответ на: комментарий от Pirr

return (*((size_t*)left) == *((size_t*)right));

Тут вы сравниваете первые sizeof(size_t) байт виртуальной таблицы класса. Спорное решение.

andreyu ★★★★★
()

Опять ты??? Не, ну это уже наглость =/

comp00 ★★★★
()
Ответ на: комментарий от nerdogeek

А такое бывает? Тады упс. Но за этим надо следить, сказано же было что это хак.

Вообще хороший вопрос для теста то, на понимание.

AIv ★★★★★
()
Ответ на: комментарий от AIv

Ничего, что перед объектом лежит не сама виртуальная таблица а только пойнтер на нее?

Нет, это хорошо. Посему решение ТСа и работает так, как он ожидает.

andreyu ★★★★★
()
Ответ на: комментарий от mashina

ptrdiff_t точно использовать нельзя. Нужно брать void*

Почему нельзя использовать ptrdiff_t?

andreyu ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.