Должен всегда возвращать указатель на класс в дизайне интерфейса?
Мне хотелось бы вернуть объект виртуального базового класса, чтобы мне не нужно было заниматься управлением памятью (идея программирования функций также стимулирует это стремление). Это означает, что я ищу некоторые вещи, как показано ниже:
class Car
{
public:
virtual int price() = 0 ;
virtual string brand() = 0 ;
}
class Interface
{
public:
virtual Car giveMeACar() = 0 ;
virtual vector<Car> listMeAllTheCars() = 0 ;
}
Однако это даже не будет скомпилировано из-за того, что Car является абстрактным интерфейсом с сообщением об ошибке:
неверный абстрактный тип возврата для функция-член 'virtual Car
giveMeACar() = 0
; поскольку следующие виртуальные функции чисты внутри 'Car
': int price()
string brand()
;
Итак, это означает, что я должен пересмотреть интерфейс на что-то вроде ниже и управлять самой памятью (удалить экземпляр после его использования) - исключая возможность использования интеллектуального указателя.
class Interface
{
public:
virtual Car* giveMeACar() = 0 ;
virtual vector<Car*> listMeAllTheCars() = 0 ;
}
Мой вопрос: это единственный вариант, который у меня есть при разработке интерфейса, где все вещи (класс) являются абстрактными?
Возвращение объекта класса интерфейса отлично подходит для Java. С++, похоже, является многословным и очень интуитивно понятным в этом аспекте. Чаще всего я считаю, что С++ является "указателем на язык программирования объектов" вместо "языка программирования объектов", потому что без указателя вы не можете получить слишком много преимуществ от программирования объектов.
Ответы
Ответ 1
В Java "возврат и объект" на самом деле семантически эквивалентен возврату указателя на объект в С++, вы пытаетесь вернуть объект по значению, что делает его копию. Вы не можете сделать копию абстрактного объекта.
Итак, хотя С++ может быть более подробным, он поддерживает различную семантику для передачи параметров и возвращающих значений, которые Java не поддерживает (возвращает значение, передает по ссылке).
С учетом сказанного вы должны вернуться с помощью умного указателя, который будет управлять памятью для вас. Другие отметили auto_ptr
с семантикой передачи прав собственности, но вы также можете использовать boost::shared_ptr
, если вы используете внутреннее распределение памяти (например, пул), shared_ptr
пользовательская функция удаления поможет вам скрыть данные об освобождении от пользователя интерфейса. Его также можно использовать в контейнерах STL (в отличие от auto_ptr).
class Car
{
public:
typedef boost::shared_ptr<Car> Ptr;
virtual int price() = 0 ;
virtual string brand() = 0 ;
};
class Interface
{
public:
virtual Car::Ptr giveMeACar() = 0 ;
virtual vector<Car::Ptr> listMeAllTheCars() = 0 ;
}
Ответ 2
Ваши наблюдения верны, нет простого способа сделать то, что вы хотите сделать. В С++ вы не можете вернуть автомобиль по значению, потому что (среди прочего) вызывающему нужно выделить место для него.
Java не принципиально отличается, просто ее синтаксис не может выразить то, что вы хотите выразить - все типы (кроме примитивных типов) имеют неявный "*", прикрепленный к ним. И у него есть сбор мусора, поэтому вам не нужно беспокоиться об управлении памятью.
Ответ 3
Вернуть a std::auto_ptr< Car >
. Он помещает объект в кучу как указатель, но удаляет его, когда он выходит из области видимости, как переменная стека.
Вариации включают boost::unique_ptr
и С++ 0x std::unique_ptr
, который отменяет auto_ptr
.
vector<Car>
не может быть заменен на vector< auto_ptr< Car > >
, потому что auto_ptr
несовместим с контейнерами. Для этого вам нужно использовать unique_ptr
.
Ответ 4
Другой вариант - использовать шаблон:
template<class CarClass>
class Interface {
virtual CarClass giveMeACar() = 0;
virtual vector<CarClass> listMeAllTheCars() = 0;
}
Ответ 5
Я думаю, этот мог бы быть полезен.
Ответ 6
Другие сообщения в ответ на ваш запрос отлично отвечают на ваш запрос. Однако у меня было несколько замечаний, которые вы можете рассмотреть
Первое наблюдение:
Избегайте подвергать деталям информацию о реализации внешнему миру.
Это относится к типу возврата
virtual vector<Car> listMeAllTheCars() = 0 ;
.
Почему пользователи класса должны знать, используете ли вы vector
или list
или что-то еще? Проверьте шаблон дизайна итератора от GOF. С++ имеет хорошую поддержку итераторов.
Второе наблюдение:
Ваше требование, похоже, подражает необходимости Factory шаблона проектирования метода, особенно когда я смотрю
virtual Car giveMeACar() = 0 ;
Возможно, вы захотите посмотреть на шаблон дизайна Factory
Третье наблюдение:
Однако наличие обеих этих функций в одном интерфейсе выглядит для меня неуместным. Возможно, вы захотите снова рассмотреть дизайн. Придерживайтесь Принцип единой ответственности.