C ++: создание неинициализированной переменной-заполнителя, а не объекта по умолчанию - PullRequest
7 голосов
/ 20 июля 2010

Я перехожу с Java на C ++ прямо сейчас, и у меня возникают некоторые трудности, когда общепринятая концепция в Java не отображается непосредственно в C ++.Например, в Java я бы сделал что-то вроде:

Fruit GetFruit(String fruitName) {
    Fruit fruit;
    if(fruitName == "apple") fruit = new Fruit("apple");
    else if(fruitName == "banana") fruit = new Fruit("banana");
    else fruit = new Fruit("kumquat"); //'cause who really wants to eat a kumquat?

    return fruit;
}

Конечно, в C ++ оператор Fruit fruit; фактически создает плод.Значит ли это, что у меня должен быть конструктор по умолчанию?Это кажется небезопасным!Что если мой фрукт по умолчанию сбежал?

Ответы [ 7 ]

10 голосов
/ 20 июля 2010

C ++ дает вам гораздо больше головной боли, когда дело доходит до создания фруктов.В зависимости от ваших потребностей, вы можете выбрать один из следующих вариантов:

1) создать Fruit в стеке и вернуть копию (вам нужен конструктор копирования), а затем указать название по умолчанию в случае, если имяне соответствует:

Fruit GetFruit(const std::string &name)
{
   if ( name == "banana" ) return Fruit("banana");
   if ( name == "apple" )  return Fruit("apple");
   return Fruit("default");
}

2) создайте Fruit в куче и позаботьтесь о том, чтобы мог быть возвращен нулевой указатель, а также не забудьте удалить этот фрукт где-нибудь и позаботьтесь о том, чтобы он был удален только один рази только его владельцем (и следите за тем, чтобы никто не держал указатель на удаленный фрукт):

Fruit* GetFruit(const std::string &name)
{
   if ( name == "banana" ) return new Fruit("banana");
   if ( name == "apple" )  return new Fruit("apple");
   return NULL;
}

3) и, наконец, используйте умный указатель, чтобы избежать многих возможных проблем с указателем (но позаботьтесь о нулеуказатели).Эта опция наиболее близка к вашему опыту программирования на Java:

typedef boost::shared_ptr<Fruit> FruitRef;

FruitRef GetFruit(const std::string &name)
{
   if ( name == "banana" ) return new Fruit("banana");
   if ( name == "apple" )  return new Fruit("apple");
   return FruitRef();
}
7 голосов
/ 20 июля 2010

Объекты в Java представлены указателями. Поскольку это широко распространено, нет специальных обозначений для указателей. В C ++ объекты могут быть представлены сами по себе или указателями, поэтому необходимо указывать указатели, когда они появляются.

Версия вашего кода на C ++:

Fruit * GetFruit(std::string fruitName) {
    Fruit * fruit = 0;
    if (fruitname == "apple") fruit = new Fruit("apple");
    else if (fruitname == "banana") fruit = new Fruit("banana");
    else fruit = new Fruit("kumquat");

    return fruit;
}

Возвращает указатель на Fruit. Вы будете иметь доступ к членам как fruit->color(), а не fruit.color(). Вы должны delete этот указатель, когда закончите с ним.

3 голосов
/ 20 июля 2010

Самый простой способ будет:

Fruit GetFruit(String fruitName) {
    if(fruitName == "apple") return Fruit("apple");
    else if(fruitName == "banana") return Fruit("banana");
    else fruit = return Fruit("kumquat"); //'cause who really wants to eat a kumquat?
}

... и прямое сопоставление будет использовать (предпочтительно «умный») указатель:

auto_ptr<Fruit> GetFruit(String fruitName) {
    auto_ptr<Fruit> fruit;
    if(fruitName == "apple") fruit = new Fruit("apple");
    else if(fruitName == "banana") fruit = new Fruit("banana");
    else fruit = new Fruit("kumquat"); //'cause who really wants to eat a kumquat?
    return fruit;
}
1 голос
/ 20 июля 2010

В C ++ есть указатели, которые неявно присутствуют в Java: есть разница между объектом (который является сущностью в памяти) и его адресом.В Java вам необходимо явно создать объект, потому что когда вы пишете

MyClass name;

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

В C ++ лучше контролируется память, и у вас есть 2 способа создания объекта: еслиВы используете оператор

MyClass object;

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

MyClass *objectPtr = new MyClass();

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

Помните, что вы должны очистить память, когда вам больше не нужен объект, или произойдет утечка памяти:

delete objectPtr;

Итак, вы можете сделать так:

MyClass *yourfunction(bool param) {
    if (param)
        return new MyClass(A);
    return new MyClass(B);
}

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

В этом случаеЛучше, если вы используете умные указатели, но сейчас действительно слишком много, чтобы сказать :) Наслаждайтесь поиском!

HIH

1 голос
/ 20 июля 2010

Чтобы отобразить ваш пример на C ++, вы должны использовать указатель.В C ++ созданный объект считается допустимым объектом (поэтому, если у вас нет ссылки на null).

Using Fruit * fruit = 0;...

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

0 голосов
/ 20 июля 2010

Объекты работают несколько иначе в Java, чем в C ++.Как вы заметили, в вашем коде вы по умолчанию создаете объект, а затем рискуете его передать позже.Чтобы внести минимальный объем изменений в ваш код:

Fruit GetFruit(std::string fruitName) {
    if(fruitName != "apple" && fruitName != "banana")
    {
        fruitName = "kumquat";
    }
    return Fruit(fruitName);
}

Однако этот код приведет к тому, что сам объект (включая все его внутренние данные) будет скопирован в результате возврата, а также если его скопироватьв дальнейшем.

Чтобы быть более Java-esque, вы бы использовали boost::shared_ptr вместо этого.Затем вы имеете дело с объектом с подсчетом ссылок, как в Java:

boost::shared_ptr<Fruit> GetFruit(std::string fruitName) {
    if(fruitName != "apple" && fruitName != "banana")
    {
        fruitName = "kumquat";
    }
    return new Fruit(fruitName);
}
0 голосов
/ 20 июля 2010

Ваша переменная fruit в Java приблизительно соответствует указателю C ++.Вы правы, вы не хотите создавать объект в стеке, вы просто хотите указатель на новый объект, который вы создаете.Так что, если вы просто измените Fruit на Fruit*, это будет работать (если вы также измените тип возвращаемого значения функции).Просто помните, что вы должны позже delete указатель, который вы вернули из этой функции, нет сборки мусора для очистки ваших new s.

...