2016-03-25 5 views
2

Я бы хотел добавить дополнительную функциональность без изменения существующего класса.Расширение существующего класса C++

Say,

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?

+1

В чем 'vtable' вы говорите? Я не вижу никаких виртуальных функций. –

+2

Возможный дубликат [Downcasting с использованием Static \ _cast в C++] (http://stackoverflow.com/questions/6322949/downcasting-using-the-static-cast-in-c) – Chad

+0

Кроме того, 'производный'' IS- A '' base', а не наоборот, но вы производите 'a' (т. Е. Тип' base') для типа 'производного' ... –

ответ

0

Вы не можете просто передать объект базового класса в унаследованный класс. Любые члены в унаследованном классе не будут созданы и инициализированы. Вам нужно начать с объекта унаследованного класса.

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

Если у вас есть указатель на базовый класс, и вы создаете его с помощью RTTI, вы можете использовать dynamic_cast для приведения к унаследованному классу, но это приведение не выполняется и возвращает NULL, если объект не принадлежит классу, который вы используете пытаясь бросить. Но обычно лучше назвать виртуальную функцию, чем использовать dynamic_cast.

+2

. Если вы собираетесь использовать 'dynamic_cast', вам понадобятся некоторые виртуальные функции, см. [FAQ: Почему dynamic_cast работают только в том случае, если класс имеет не менее 1 виртуальный метод?] (http://stackoverflow.com/questions/4227328/faq-why-does-dynamic-cast-only-work-if-a-class-has-at- наименее 1-виртуальный метод). –

1

Способ, которым вы распространяете 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 может привести к тому, что вы не будете напрямую связываться с интерфейсом.

+1

Зачем вам делать 'Derived' до' Serializable'? Разве функция 'serialize()' уже доступна в 'Derived'? –

+0

@MikeJiang вы правы, на самом деле нет причин бросать в этом случае. В этом сценарии я бы предпочел написать общую функцию, которая имеет общий интерфейс, такой как «сериализация» или функция шаблона. – Freddy

2

Если вы можете написать функцию сериализации в производном классе, не касаясь частных или защищенных членов, тогда вы должны просто сделать ее свободной. Это все решает.

+0

В моем приложении есть другие производные классы, унаследованные от 'base'. И мне нужно сделать для них такую ​​же сериализацию. Все эти производные объекты хранятся как «базовые *», поэтому свободная функция не может достигать вызовов полиморфных функций в качестве функции-члена ((например, «obj.serialze()»). –