Вам больше нечего делать. Код, который вы предоставляете, делает именно это.
Когда вы получаете указатель на базовый класс, поскольку метод был объявлен в базовом классе и является виртуальным, фактическая реализация будет найдена в таблице виртуальных функций класса и вызвана соответствующим образом.
So
base* base_ptr = new A();
base_ptr->get();
Будет вызывать A::get(). Вы не должны возвращать null из реализации (ну, вы не можете, так как null не может быть преобразован в std::list‹ что-то в любом случае). Вы должны предоставить реализацию в A/B, так как метод базового класса объявлен чисто виртуальным.
ИЗМЕНИТЬ:
вы не можете иметь только A, возвращающий std::list‹ что-то ›, но не B, поскольку B также наследует базовый класс, а базовый класс имеет чисто виртуальный метод, который должен быть переопределен в производном классе. Наследование от базового класса является отношением «является». Единственным другим способом, который я мог видеть, было бы частное наследование от класса, но это предотвратило бы преобразование производного в базовое.
Если вы действительно не хотите, чтобы B имел метод get, не наследуйте его от base. Некоторые альтернативы:
Вызов исключения в B::get(): Вы можете выдать исключение в B::get(), но убедитесь, что вы хорошо объяснили свое обоснование, поскольку оно противоречит интуиции. ИМХО, это довольно плохой дизайн, и вы рискуете запутать людей, используя свой базовый класс. Это дырявая абстракция, и ее лучше избегать.
Отдельный интерфейс: вы можете разбить базу на отдельный интерфейс:
class IGetSomething
{
public:
virtual ~IGetSomething() {}
virtual std::list<something> Get() = 0;
};
class base
{
public:
// ...
};
class A : public base, public IGetSomething
{
public:
virtual std::list<something> Get()
{
// Implementation
return std::list<something>();
}
};
class B : public base
{
};
Множественное наследование в этом случае допустимо, поскольку IGetSomething — это чистый интерфейс (у него нет переменных-членов или нечистых методов).
EDIT2:
Основываясь на комментариях, кажется, что вы хотите иметь общий интерфейс между двумя классами, но при этом иметь возможность выполнять некоторые операции, которые выполняет одна реализация, но не предоставляет другая. Это довольно запутанный сценарий, но мы можем черпать вдохновение из COM (пока не стреляйте в меня):
class base
{
public:
virtual ~base() {}
// ... common interface
// TODO: give me a better name
virtual IGetSomething *GetSomething() = 0;
};
class A : public Base
{
public:
virtual IGetSomething *GetSomething()
{
return NULL;
}
};
class B : public Base, public IGetSomething
{
public:
virtual IGetSomething *GetSomething()
{
// Derived-to-base conversion OK
return this;
}
};
Теперь вы можете сделать следующее:
base* base_ptr = new A();
IGetSomething *getSmthing = base_ptr->GetSomething();
if (getSmthing != NULL)
{
std::list<something> listOfSmthing = getSmthing->Get();
}
Это запутанно, но есть несколько преимуществ этого метода:
Вы возвращаете общедоступные интерфейсы, а не конкретные классы реализации.
Вы используете наследование для того, для чего оно предназначено.
Его трудно использовать по ошибке: base не предоставляет std::list get(), потому что это не обычная операция между конкретной реализацией.
- #P17# <блочная цитата> #P18# блочная цитата>
Это было бы возможно, но плохой дизайн, это все равно, что иметь торговый автомат, который может продавать кока-колу и пепси, за исключением того, что он никогда не продает пепси; это вводит в заблуждение, и его лучше избегать.
Как насчет того, чтобы просто вернуть boost::Optional‹ std::list‹ что-то › › ? (как предложил Андрей)
Я думаю, что это лучшее решение, лучше, чем возврат и интерфейс, который иногда может быть NULL, а иногда нет, потому что тогда вы явно знаете, что это необязательно, и в этом не будет ошибки.
Недостатком является то, что он помещает boost внутри вашего интерфейса, чего я предпочитаю избегать (я могу использовать boost, но клиенты интерфейса не должны быть вынуждены использовать boost).
person
Julien Lebot
schedule
06.07.2012
class A
, не должно ли это быть только вclass A
, а не вBase
? - person Naveen   schedule 06.07.2012base
. - person Nick   schedule 06.07.2012class B
не должен быть подклассомBase
. Что-то может быть сломано в конструкции. - person betabandido   schedule 06.07.2012base
, вы всегда можете привести его кA *
, либо используяstatic_cast
, либо используяdynamic_cast
, в зависимости от того, знаете ли вы конкретный тип. (Редактировать: измененоA
наA *
.) - person   schedule 06.07.2012A
иB
, например, в одном списке. - person Nick   schedule 06.07.2012B
концептуально явно не являетсяbase
, поэтому не должно быть производным отbase
. - person razlebe   schedule 06.07.2012Make sure public inheritance models "is-a"
. Все, что верно для базового класса, должно быть верно и для производного класса. ЕслиB
действительно являетсяBase
и вызовget()
наB
недействителен, то вызовget()
наBase
должен быть недействителен. - person BoBTFish   schedule 06.07.2012A
иB
в одном списке. И я полагаю, что вы сейчас делаете итерацию по списку (имеется в виду, что вы получаетеbase *
) и пытаетесь вызватьget()
? Что вы хотите, чтобы произошло, когда ваш список содержитB
объектов? - person   schedule 06.07.2012B
— это просто тип объекта, который имеет что-то общее сA
, но не все. Не могли бы вы лучше перефразировать свой вопрос? - person Nick   schedule 06.07.2012B::get()
не должен работать, ваш список может содержать объектыB
, и вы не хотите проверять, является ли объект объектомA
(dynamic_cast
), так что вы хотите, чтобыbase->get()
делал, когдаbase
указывает наB
объект? - person   schedule 06.07.2012