Назначение общего списка C ++ - PullRequest
2 голосов
/ 30 апреля 2010

Я явно слишком долго застрял на земле Java ... Возможно ли сделать C ++ эквивалент следующего кода Java:

interface Foo {}

class Bar implements Foo {}

static List<Foo> getFoo() {
  return new LinkedList<Foo>();
}

static List<Bar> getBar() {
  return new LinkedList<Bar>();
}

List<? extends Foo> stuff = getBar();

Где Foo - это подкласс Bar.

Так что в C ++ ....

std::list<Bar> * getBars()
{
  std::list<Bar> * bars = new std::list<Bar>;
  return bars;
}

std::list<Foo> * stuff = getBars();

Надеюсь, что это имеет смысл ....

Ответы [ 6 ]

4 голосов
/ 30 апреля 2010

Изменение

std::list<Bar> & getBars()

до

std::list<Bar> getBars()

В принципе это будет возвращаться по значению, но есть механизм исключения копирования, поэтому оптимизирующие компиляторы должны иметь возможность оптимизировать

std::list<Bar> bars (getBars());

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

Кроме того, в C ++ 0x есть конструктор перемещения, поэтому приведенный выше код эффективен, даже если копирование / перемещение по какой-либо причине не исключено.

РЕДАКТИРОВАТЬ для пропущенного вопроса подкласса:

В целом это невозможно сделать чисто в C ++ (я предполагаю, что Foo является суперклассом из Bar, иначе то, что вы делаете, не имеет особого смысла). Возможно, это возможно с некоторой reinterpret_cast черной магией, но я настоятельно рекомендую против этого.

Ближайшее приближение, вероятно, таково (с учетом getBars() соответственно):

std::list <Bar*>  bars (getBars ());
std::list <Foo*>  foos (bars.begin (), bars.end ());

Однако это накладывает на вас некоторое нетривиальное бремя управления памятью. Использование некоторых указателей может помочь, но, насколько я помню, в контейнерах можно использовать только shared_ptr.

Наконец, иерархия классов Foo / Bar должна использовать виртуальные функции, в противном случае то, что вы хотите сделать, почти наверняка не сработает (т.е. если вы действительно не хотите игнорировать любые переопределения в подклассе).

3 голосов
/ 30 апреля 2010

Нет, на мой взгляд, это не имеет смысла в C ++.

Сначала вы возвращаете ссылку, которой больше не существует. Чтобы избежать этого, вы можете передать свой std :: list в качестве ссылочного параметра, который будет изменен в функции, как

void fillFoos( std::list< Foo > & foos )

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

Но если вы используете наследование, все ваши foos и бары должны быть указателями (и если вы можете умные как указатели shared_ptr из boost или tr1). Но это не значит, что копия работает.

Я не совсем уверен в том, что вы хотите сделать, но в этом случае транспонирование из JAVA в C ++ не работает. Если вы создадите foos, у них будет все из баров автоматически.

std::list< Foo > foos; // just work fine

Если вы хотите список баров, построенных как foos:

std::list< Bar * > bars;
bars.push_back( new Foo() );

Или, как я бы сказал, в реальном C ++-коде с shared_ptr:

typedef boost::shared_ptr< Bar >;
typedef boost::shared_ptr< Foo >;
typedef std::list< BarPtr > BarList;

BarList bars;

bars.push_back( FooPtr( new Foo() ) );
2 голосов
/ 30 апреля 2010

Либо я здесь что-то не так делаю, либо ваш Java-код не работает. Я только что попробовал. Не работает Причина в том, что универсальные контейнеры в Java не являются ковариантными (это означает, что он также не работает, если Bar является подклассом Foo). Во-вторых, если Foo является подклассом Bar, то вы можете назначить Foo ссылки на Bar, но не наоборот (ни в Java, ни в C ++).

Контейнеры в C ++ также не являются ковариантными. Но вы можете сделать

std::list<Foo*> FooList;
// fill FooList;
std::list<Bar*> BarList(FooList.begin(), FooList.end());

, если Foo является подклассом Bar. Однако это приведет к тому, что все указатели в FooList будут скопированы в BarList. Это означает, что если вы измените FooList впоследствии, добавив или удалив элементы, эти изменения не будут отражены в BarList.

2 голосов
/ 30 апреля 2010

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

std::list<Bar> getBars()
{
    std::list<Bar> Bars;
    return Bars;
}

или

std::list<Bar>* getBars()
{
    return new std::list<Bar>();
}

std :: list поддерживает копирование объектов другого списка, но только если этот список имеет тот же тип, то есть: вы не можете копировать из std::list<Bar> в std::list<Foo> неявно, но вы можете копировать из std::list<Foo> в std::list<Foo>.

1 голос
/ 30 апреля 2010

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

0 голосов
/ 30 апреля 2010

Кроме того, новый std :: list вернет указатель, а не ссылку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...