Возвращен адрес локальной переменной - можно игнорировать предупреждение без ущерба, но как правильно? - PullRequest
0 голосов
/ 07 апреля 2019

У меня есть класс Particle:

class Particle {
private:
    float x, y, z;
    // ...
public:
    // ...
    float* getPos() {
        float p[3] = {x, y, z};
        return p;
    }
    // ...
};

Я бы назвал этот метод следующим образом:

Particle a = Particle();
// ...
float* pos = a.getPos();

И затем с помощью элементов от 10000 * до pos[2] будем ссылаться на элементы позиции.

g ++ Носится предупреждение, как указано в заголовке.Но функциональность именно такая, как я хочу: возвращать массив.Почему существует предупреждение и существует ли «правильный» способ сделать это?

Ответы [ 5 ]

2 голосов
/ 07 апреля 2019

Вы не можете вернуть такой C-массив, вместо этого верните std::array:

std::array<float, 3> getPos() {
    std::array<float, 3> p = {x, y, z};
    return p;
}

Для этого вам нужно будет включить <array>.

1 голос
/ 12 апреля 2019

Лично я бы здесь пропустил std::array / std::vector, потому что в вашем конкретном случае позиция каждого значения накладывает независимое значение.Как правило, типы последовательности имеют порядок, tuple имеют структуру;если количество элементов является фиксированным (и часто неоднородным), а сортировка (или иным образом переупорядочение значений) по своей сути бессмысленна (например, в случае координаты, при перестановке значений x и y изменяется значение , означающее ), тогда tuple имеет больше смысла.

В этом случае вы можете просто объявить:

std::tuple<float, float, float> getPos() {
    // C++17 or higher allows list initialization
    return {x, y, z};

    // Pre-C++17 you use the std::make_tuple helper
    return std::make_tuple(x, y, z);
}

Преимущество заключается в том, что вы можете затем распаковать результат в вызывающей программе.легко, с помощью std::tie:

float x, y, z;

std::tie(x, y, z) = a.getPos();

или на C ++ 17 или выше со структурированными привязками, это даже лучше, поскольку вы можете объявлять и инициализировать переменные с помощью auto, а не объявлять с помощьюявные типы, затем переназначение с помощью tie:

auto [x, y, z] = a.getPos();

Вы можете сохранить сам tuple и использовать std::get, если хотите, но распаковка под полезными именами вместо неясных std::get индексов обычно делаетдля более чистого кода.

1 голос
/ 07 апреля 2019

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

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

class Particle {
private:
    float pos[3];
    // ...
public:
    // ...
    float* getPos() {
        return pos;
    }
    // ...
};

. Вместо этого вы можете вернуть vector<float>.Вы можете вернуть array<float,3> вместо этого.Вы можете спросить себя, зачем вам это нужно.

0 голосов
/ 07 апреля 2019

Я бы предположил, что ваша функция свидетельствует о плохом дизайне. Предоставьте getter методы, позволяющие пользователю вашего класса получать доступ к переменным-членам:

class Particle {
private:
    float x, y, z;
public:
    float GetX() const { return x; }
    float GetY() const { return y; }
    float GetZ() const { return z; }
};

Учитывая const Particle a, это позволит вам инициализировать массив следующим образом: const float pos[] = { a.GetX(), a.GetY(), a.GetZ() }

Создание Particle метода, позволяющего пользователю заполнять float[], побудит пользователя к одной из следующих плохих практик:

  1. float* Particle::GetPos() const { return new[3]{ x, y, z }; } создает динамическую память без четкого информирования вызывающей стороны о необходимости освобождения памяти
  2. array<float, 3U> Particle::GetPos() const { return { x, y, z }; } требует выделения и создания временного для заполнения float[]
  3. void Particle::GetPos(float* param) const { param[0] = x; param[1] = y; param[2] = z; } упускает возможность для постоянных массивов и влечет за собой потенциальное злоупотребление вызывающим абонентом, так как не ясно, что param должно иметь место как минимум 3 float s
0 голосов
/ 07 апреля 2019

p[3] будет уничтожено, когда оно выйдет из области видимости, поэтому вы не должны возвращать указатель на него. Либо верните std::array<float, 3> по значению, либо рассмотрите возможность создания класса для позиций, а также верните объект Position или ссылку на него. Пример:

struct Position {
    float x, y, z;
};

class Particle {
private:
    Position m_pos;
    // ...
public:
    // ...
    Position const& getPos() const { return m_pos; }
    // ...
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...