2011-05-14 3 views
4

Я пытаюсь создать класс, который будет служить базовым объектом, который затем будет подклассифицирован (= реализован) для обслуживания различных целей.C++ Наследование с чистыми виртуальными функциями

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

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

class BaseItem 
{ 
public: 
    virtual std::string getDifferences(const BaseItem& item) = 0; 
} 

Таким образом, в производном классе, я хотел бы сделать:

class DerivedClass : public BaseItem 
{ 
public: 
    virtual std::string getDifferences(const DerivedClass& item) = 0; 
private: 
    std::string derivedItemCustomObject; 
} 

, который, конечно, компилятор не будет принимать. Конечно, я мог бы сделать это BaseItem, но тогда я не могу использовать какие-либо объекты в производном классе.

Должен ли я использовать литье, чтобы выполнить это?

Пожалуйста, дайте мне знать, если мои намерения/вопрос не ясны.

+0

Да, общий способ сделать это - сделать параметр BaseItem и, возможно, бросить его по мере необходимости в производных классах, используя dynamic_cast. –

+0

@ybungalobill @BoPersson [Посмотреть здесь.] (Http: //meta.stackoverflow.com/q/2950/152657) –

+1

@muntoo: [посмотреть здесь] (http://meta.stackexchange.com/questions/2950/should-hi-thanks-and-taglines-and-salutations-be-removed-from -posts/2956 # 2956) – ybungalobill

ответ

3

NO необходимо изменить подпись функции. Посмотрите на следующее:

class BaseItem 
{public: 
    virtual std::string getDifferences(const BaseItem& item) = 0; 
}; 

class DerivedClass : public BaseItem 
{public: 
    virtual std::string getDifferences(const BaseItem& item) // keep it as it's 
    { 
     const DerivedClass& derivedItem = static_cast<const DerivedClass&>(item); 
    } 
}; 

Можно использовать static_cast<> без страха, потому что, DerivedClass::getDifferences() вызывается только для DerivedClass объекта. Для того, чтобы проиллюстрировать,

BaseItem *p = new DerivedClass; 
DerivedClass obj; 
p->getDifferences(obj); // this always invoke DerivedClass::getDifferences 

Если вы обеспокоены тем, что когда-то вы могли бы в конечном итоге прохождение любого другого объекта производного класса в качестве аргумента метода, а затем использовать dynamic_cast<> вместо этого и выбросить исключение, если что отливка не удается.

+0

Это, кажется, самый простой способ добиться того, что я искал, спасибо! –

+0

Спасибо @iammilind, ты сделал мой день :) – Eric

+0

@ Эрик, приятно слышать ... это помогает. Тем не менее, убедитесь, что вы передаете правильный объект типа функции. – iammilind

1

Одним из способов достижения этой цели является использование шаблона и иметь параметр будет типа производного типа

template <typename T> 
class BaseItem { 
public: 
    virtual std::string getDifferences(const T& item) = 0; 
}; 

class DerivedClass : public BaseItem<DerivedClass> { 
public: 
    virtual std::string getDifferences(const DerivedClass& item) { 
    // Implement it here 
    } 
}; 
0

Вы должны использовать бросок от BaseItem до DerivedClass + проверки во время выполнения, если данный BaseItem является экземпляр DerivedClass ,

2

Непонятно, чего вы пытаетесь достичь. Предположим, что компилятор позволил вам сделать это (или вы делаете это с помощью средств гипсе), то было бы открыть следующую дыру в системе типов:

class BaseItem 
{ 
public: 
    virtual std::string getDifferences(const BaseItem& item) = 0; 
}; 

class DerivedClass : public BaseItem 
{ 
public: 
    virtual std::string getDifferences(const DerivedClass& item) 
    { 
     item.f(); 
     // ... 
    } 

    void f() const {} 
}; 

class DerivedClass2 : public BaseItem 
{ 
public: 
    virtual std::string getDifferences(const DerivedClass2& item) { ... } 
}; 

void g() 
{ 
    BaseItem* x = new DerivedClass; 

    // oops, calls DerivedClass::f on an instance of DerivedClass2 
    x->getDifferences(DerivedClass2()); 
} 

Ваш дизайн, вероятно, неправильно.

+0

На самом деле это не дизайн, я надеюсь, что ответы здесь помогут мне придумать хороший дизайн. –

2

Я предполагаю, что компилятор принимает, но DerivedClass :: getDifferences не переопределяет BaseItem :: getDifferences. Вот способ добиться того, что вы хотите, по-видимому

template <typename T> 
class DerivedHelper: public BaseItem { 
public: 
    virtual std::string getDifferences(const BaseItem& item) { 
     getDifferences(dynamic_cast<const T&>(item)); 
    } 
    virtual std::string getDifferences(const T& item) = 0; 
}; 

class DerivedClass : public DerivedHelper<DerivedClass> 
{ 
public: 
    // not more needed but providing it will hide getDifferences(const BaseItem& item) 
    // helping to statically catch some cases where a bad argument type is used. 
    virtual std::string getDifferences(const DerivedClass& item) = 0; 
private: 
    std::string derivedItemCustomObject; 
}; 

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

+1

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

+1

Я не уверен насчет скрытия после того, как я полюбил его. Может быть полезно, но я несерьезный. struct SillyExample {BaseItem & x; template void operator() (T & n) {n.getDifferences (x); }}; // Вы хотите предотвратить это? –

+0

@Fred, я иногда не уверен, что хочу статического ввода :-) Всегда такой же компромисс между выразительностью и безопасностью. – AProgrammer

Смежные вопросы