Я хотел бы добавить дополнительную функциональность без изменения существующего класса.
Сказать,
class base{
public:
int i;
base(){i = 1;}
virtual void do_work(){ //Do some work}
};
Если я хочу добавить serialization
функция-член к нему, я просто создаю производный класс
class derived:public base{
public:
void serialize();
};
void derived::serialize(){
cout << "derived class" << endl;
}
И мне нужно справиться с существующими base
объекты, например.
int main(){
base a;
derived & b = static_cast<derived &>(a);
b.serialize();
}
Этот пример работает без проблем. Но я знаю downcast
через static_cast
это то, чего следует избегать в целом.
Но я хотел бы знать, если downcast
для этого конкретного случая использования можно считать безопасным, так как derived
класс имеет только одну дополнительную функцию-член. Будет ли у него какое-то потенциально неопределенное поведение для доступа vtable
?
Если вы можете написать функцию сериализации в производном классе, не затрагивая закрытые или защищенные члены, тогда вы просто должны сделать ее свободной функцией. Это решает все.
То, как вы расширяете Base
вы не используете vtable
потому что у тебя нет virtual
методы. Может быть проще думать об этом как Derived has A Base
; Что вы создали новый класс, который содержит Base
переменная-член.
Мое предложение.
Функция шаблона
Я лично пошел бы с шаблонной функцией. Вы можете сохранить всю работу в своем первоначальном вопросе, и избежать необходимости добавления virtual
звонки в ваш класс.
template<typename T>
void serialize_object(T& t)
{
t.serialize()
}
А потом по вашему примеру.
Derivied d;
serialize_object(d);
Большим преимуществом является то, что вы не добавляете приведение во время исполнения здесь. Компилятор сообщит вам, если вы передадите объект, у которого нет метода serialize
,
Go Virtual
Если вы действительно хотите справиться с этим через виртуальный интерфейс, сделайте это.
struct Serializable{
virtual void serialize()=0;
virtual ~Serializable(){}
}
class Derived : public Serializable {
public:
void serialize() override;
}
void Derivied::serialize()
{
std::cout << "Yah\n";
}
Тогда в вашем коде.
Derivied d;
Serializable& s = dynamic_cast<Serializable&>(d);
Тем не менее, большая проблема заключается в том, кто является владельцем вашего базового класса? Они предоставили виртуальный дтор? Если нет, то используя std::unique_ptr
или же std::shared_ptr
может заставить вас не иметь дело напрямую с интерфейсом.
Вы не можете просто привести объект базового класса к унаследованному классу. Любые члены в унаследованном классе не будут созданы и инициализированы. Вам нужно начать с объекта унаследованного класса.
Конечно, вы можете пойти другим путем и вызвать функции базового класса из функции класса, унаследованного от базового, потому что объект базового класса всегда будет присутствовать как часть наследуемого объекта класса, по определению.
Если у вас есть указатель на базовый класс, и вы строите с помощью RTTI, вы можете использовать dynamic_cast для приведения к унаследованному классу, но это приведение завершится неудачно и вернет NULL, если объект не принадлежит к классу, который вы пытаетесь привести к , Но обычно лучше вызывать виртуальную функцию, чем использовать dynamic_cast.