C ++ - инициализировать переменную, передавая ссылку на функцию? - PullRequest
1 голос
/ 05 февраля 2012

Можно ли инициализировать переменную из возвращаемого параметра (по ссылке)?Скажем, у меня есть что-то вроде:

Car c;    // <- don't want to create a new Car here!
if (findCar("beetle", c)) {
    ...
}

, где, если findCar успешно, возвращается true и заполняет c:

bool findCar(string name, Car& out) {
    ...
    // return true if found
    out = thecar;
    return true;
}

Теперь мой класс Car не имеетКонструктор с 0 аргументами, поэтому приведенный выше код не может быть скомпилирован.Есть ли способ сохранить c неинициализированным до вызова findCar?

Решения, о которых я подумал:

  • добавление дешевого конструктора с 0 аргументами в Car
  • переключиться на указатели (которых я бы лучше избегал)

Ответы [ 6 ]

4 голосов
/ 05 февраля 2012

Вроде.Проблема в том, что ссылка обязательно должна ссылаться на реальный объект.Итак, если вы вернетесь по ссылке, тогда кто-то должен создать объект для этой возвращенной ссылки.Поэтому, если вы не можете найти соответствующий объект, возвращать ссылку не имеет смысла.Если вы передаете ссылку, то сначала вы должны создать объект, чтобы аргумент ссылался на него.

Вы можете обойти это, например, следующим образом:

Car &findCar(const string &name) {
    ...
    // return if found, else throw
    if (found it) {
        return thecar; // assuming `thecar` means some already-existing object,
                       // if it's a local variable then return by value!
    } else  {
        throw std::runtime_error(name);
    }
}

Вызывающий объект выполняет

Car c = findCar("beetle");

или Car &c = findCar("beetle");, если они хотят «увидеть» фактический найденный объект, а не его копию.Если findCar хочет, чтобы вызывающие абоненты когда-либо видели только копию, а не какой-то внутренний объект, тогда, конечно, вы можете вернуть по значению, а не по ссылке - разница составляет один & в сигнатуре функции.кто-то где-то должен обработать исключение.

Если вы предпочитаете избегать исключений, то правильной вещью, которую нужно вернуть из функции find, является указатель (или другой итератор).Это то, что стандартные контейнеры и алгоритмы делают при поиске, и есть специальные значения (конечные итераторы, или вы можете использовать нулевые указатели), которые означают «не найден».

2 голосов
/ 05 февраля 2012

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

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

1 голос
/ 05 февраля 2012

Объект инициализируется, как только вы объявляете его как переменную.

Если вы хотите позднюю инициализацию, вы должны использовать указатель и:

Car *c;

bool findCar(string name, Car * &out){
  ...
  out = new Car();
}

не забудьте удалить его

0 голосов
/ 15 января 2013

Я не очень опытен в C ++, но знаю, что это не самое чистое решение.

Однако вот подход:

bool findCar(string name, Car **out) {
    // first make out to null
    *out = NULL;
    ...
    // if found
    if(found){
       *out = &thecar;
       return true;
    }

    // otherwise return false. Notice that out remains pointing null
    return false;
}

Теперь в любом месте кода вы должны иметь:

Car *c;    // <- You don't create a new Car here!
if (findCar("beetle", &c)) {
    ...
}

Надеюсь, вы найдете это полезным

0 голосов
/ 05 февраля 2012

Вы можете вернуть Car из вашей функции:

struct Car
{
    Car():_isValid(false){}
    Car(const Car&);
    bool isValid();
};

Car findCar (const string& name);


Car car = findCar("beetle");
if (car.isValid()){
...
}

другой вариант - throw исключение, если findCar не может найти автомобиль:

struct Car
{
    Car(const Car&);
};

Car findCar (const string& name) {
    ....
    if (noCarFound) throw CarNotFoundException();
    ....
}



try{
    Car car = findCar("beetle") {

}
catch (CarNotFoundException e) {
    ....
}

Или создать Car конструктор, который принимает string

0 голосов
/ 05 февраля 2012

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

car* find_car(string name) {

   ...
   return nullptr;
}

car* c = find_car(...);
if (c != nullptr) {
    ...
}

или использовать исключения, что, вероятно, не то, что вы хотите

car find_car(string name) {
   ...
   throw new runtime_error("car not found");
}

Фактически, объявление его в качестве справочного означает, что вы гарантируете, что объект был сконструирован, так что требование должно быть выполнено. Я бы использовал указатели лично.

...