Comments 7
Если честно, не понял по поводу slice:
Можете поподробней рассказать вот это место:
У вас же
Вывод:
Компилятор сделает срез базового типа по отнаследованному. И если указатель увеличился с 1, значит объект был срезан.
Можете поподробней рассказать вот это место:
И если указатель увеличился с 1, значит объект был срезан
У вас же
Message
— полиморфный и вы передаёте его по ссылке, можно восстановить объект. Как-то так:struct A
{
int a_;
A(int a)
: a_(a)
{
}
virtual ~A()
{
}
};
struct B :
public A
{
int b_;
B(int a, int b)
: A(a)
, b_(b)
{
}
};
struct C :
public A
{
int c_;
C(int a, int c)
: A(a)
, c_(c)
{
}
};
void test(const A& a)
{
try
{
const B& b = dynamic_cast<const B&>(a);
std::cout << b.a_ << " " << b.b_ << std::endl;
}
catch(const std::bad_cast& e)
{
std::cout << "test: @a is not B: " << e.what() << std::endl;
}
}
int main()
{
test(C(10, 20));
test(B(10, 30));
return 0;
}
Вывод:
test: @a is not B: Bad dynamic_cast!
10 30
0
Да, спасибо. Я не совсем верно употребил по смыслу slice. Я отбрасываю из решения частные случаи slice. Там где указатель на класс наследника и указатель на базовый класс начинают отличаться. В вашем примере с этим всё хорошо (https://ideone.com/9vzMCE).
По смыслу восстановить объект с dynamic_cast можно. И это вариант более полного решения, нежели приведённое. Я скорее хотел обойтись без dynamic_cast, используя фактически reinterpret_cast для ссылок. И поэтому в таком решении приходится отсеивать такие случаи.
По смыслу восстановить объект с dynamic_cast можно. И это вариант более полного решения, нежели приведённое. Я скорее хотел обойтись без dynamic_cast, используя фактически reinterpret_cast для ссылок. И поэтому в таком решении приходится отсеивать такие случаи.
0
Со срезкой, как мне кажется, проблема надумана. Мы же копированием объектов не занимаемся?
Поэтому вполне можем себе позволить смещение базы туда и обратно:
Но для этого надо избавиться от реинтерпретов.
А чтобы избавиться от реинтерпретов, в свою очередь, можно воспользоваться идиомой невиртуального интерфейса: вместе с указателями на объект и его метод сохранять указатель на вспомогательную функцию, которая умеет правильно вызывать метод объекта.
Сейчас попробую сделать эскиз…
Поэтому вполне можем себе позволить смещение базы туда и обратно:
class Base { ..... };
class Derived : ....., Base, ..... { ..... };
Derived d;
Derived* pd = &d;
Base* pb = /* static_cast<Base*> */ pd;
assert( (void*)pb != (void*)pd ); // пусть у нас база смещена, иначе как-то банально будет...
Derived* pd2 = static_cast<Derived*>(pb);
assert( pd2 == pd ); // это условие должно выполниться в любом случае
Но для этого надо избавиться от реинтерпретов.
А чтобы избавиться от реинтерпретов, в свою очередь, можно воспользоваться идиомой невиртуального интерфейса: вместе с указателями на объект и его метод сохранять указатель на вспомогательную функцию, которая умеет правильно вызывать метод объекта.
Сейчас попробую сделать эскиз…
0
ideone.com/QZrLxf — пример делегата, устойчивого к сдвигу базы
Получаем:
#include <cstdio>
// обобщённый указатель на метод
struct GenericObject; // класс должен быть неизвестен заранее, тогда компилятор сделает наиболее общий тип метода
typedef void (GenericObject::*GenericMethod)(GenericObject const&);
template<class HandlerBase, class ArgBase>
struct CallTraits
{
typedef void(*CallFunc)(HandlerBase*, GenericMethod, ArgBase const&);
template<class TheHandler, class TheArg>
static void callfunc(HandlerBase* h, GenericMethod m, ArgBase const& a)
{
typedef void (TheHandler::*TheMethod)(TheArg const&);
TheHandler* th = (TheHandler*)h;
TheMethod tm = (TheMethod)m;
TheArg const& ta = (TheArg const&)a;
printf(" < %p->...(*%p)\n", h, &a);
printf(" > %p->...(*%p)\n", th, &ta);
(th->*tm)(ta);
}
struct Delegate
{
HandlerBase* h;
GenericMethod m;
CallFunc c;
};
template<class TheHandler, class TheArg>
static Delegate make_delegate(TheHandler* th, void (TheHandler::*tm)(TheArg const&))
{
Delegate d = { (HandlerBase*)th, (GenericMethod)tm, &callfunc<TheHandler,TheArg> };
return d;
}
static void call_delegate(Delegate const& d, ArgBase const& a)
{
d.c(d.h, d.m, a);
}
};
struct A0 { int x; virtual ~A0() {} };
struct A1 { int y; };
struct A : A0, A1 {}; // хорошенько сдвинем базу...
struct H0 { int x; virtual ~H0() {} };
struct H1 { int y; virtual ~H1() {} }; // и усложним себе жизнь, добавив виртуальные функции
struct H : H0, H1
{
virtual void foo(A const& a)
// чтобы жизнь мёдом не казалась! виртуальная функция с большей вероятностью грохнется, если мы напутаем
{
printf(" = %p->foo(*%p)\n", this, &a);
}
};
int main()
{
typedef CallTraits<H1, A1> CT;
A a; H h;
printf("directly:\n");
h.foo(a);
printf("delegated:\n");
CT::Delegate d = CT::make_delegate(&h, &H::foo);
printf("delegate: %p %p %d %d\n", d.h, d.c, sizeof(d.c), sizeof(d.m));
CT::call_delegate(d, a);
}
Получаем:
directly:
= 0xbf9fb780->foo(*0xbf9fb774)
delegated:
delegate: 0xbf9fb788 0x80487a0 sizeof(fun)=4 sizeof(method)=8
< 0xbf9fb788->...(*0xbf9fb77c) -- видно, что и обработчик, и аргумент сдвинуты...
> 0xbf9fb780->...(*0xbf9fb774) -- мы их корректно восстанавливаем...
= 0xbf9fb780->foo(*0xbf9fb774) -- и вызываем правильную функцию!
+1
Sign up to leave a comment.
О вольностях в ссылках или простейший обмен сообщениями