C / C ++ освобождает указатель, вызывая ссылку или напрямую

0
struct State {
    int* maxLiters;
    int* nowLiters;
} parentState;

void makeState(State& s) {
    if ((s.maxLiters = (int*)malloc(cups*sizeof(int))) == nullptr) {
        error();
    }
    if ((s.nowLiters = (int*)malloc(cups*sizeof(int))) == nullptr) {
        error();
    }
}

void delState(State& s) { // or delState(State s) ?
    free(s.maxLiters);
    free(s.nowLiters);
}

Я всегда был кодированием на C, и я действительно начинаю делать C++. Приносим извинения за использование "malloc".

В функции delState я передал структуру по ссылке. Я немного не уверен, можно ли передавать это по значению, как в комментарии. Обычно в CI это делается с помощью указателя, чтобы последовательно устанавливать "&" при вызове создания и удаления функций. Поскольку с эталонным параметром я не должен набирать '&', у меня возникает соблазн сделать чистую функцию по умолчанию. Думая об этом, я сам говорю "хорошо", потому что в любом случае, по ссылке или по значению, "свободная" функция получит тот же адрес памяти. Но я просто волнуюсь, потому что я этого никогда не делал.

Полезными были бы любые разъяснения, за которые спасибо заранее.

  • 1
    Передача по значению не будет правильной, все операции будут применяться к копии s . Также просто используйте std::vector<int> вместо того, чтобы самостоятельно управлять памятью.
  • 1
    Предполагая, что State не делает глубоких копий, это не имеет значения. free действует на указатель, который копируется. Конечно, делая это ссылкой, мы могли бы установить указатели на NULL чтобы избежать двойного освобождения.
Показать ещё 6 комментариев
Теги:
pointers

3 ответа

4
Лучший ответ

Если вы хотите использовать C++, лучше узнать "способ C++" делать вещи. В этом случае вы действительно не должны вызывать функции для создания и удаления своего состояния для вас, вместо этого вы должны использовать конструкторы классов и деструкторы. Кроме того, вместо использования массивов обычно лучше всего использовать std :: vector. Для вашего примера это будет выглядеть так:

class State {
public:
    State (size_t cups) 
       : maxLiters(cups), nowLiters(cups) //reserve "cups" amount of space in the vectors
    { }

    ~State () =default; //default destructor, will call the vector destructors and delete the data automatically
    State (const State &other) =default //default copy constructor, will copy the contents of the vectors into a new State object
    State &operator= (const State &other) =default //default assignment operator, will overwrite the contents of this object with the new one

    std::vector<int> maxLiters;
    std::vector<int> nowLiters;    
};

Затем, если вы хотите обрабатывать ошибки из памяти, вы сделали бы это при создании объекта состояния:

try
{
    State s(12);
    ...
}
catch (std::bad_alloc &ba)
{
    error();
}

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

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

Вы также должны подумать об инкапсулировании ваших векторов, объявив их частными и предоставляя им getters/setters. Это хорошая практика, даже если вы заявляете как получатели, так и сеттеры для согласованности интерфейса.

  • 0
    Обратите внимание: кажется, что он не пытается выделить простой int, а массив из них ...
  • 0
    Ой, я не видел эти cups*
Показать ещё 3 комментария
2

Я думаю, что совет использовать конструкторы/деструкторы вместе с вектором - это тот, который вы должны предпринять, чтобы решить эту проблему. Тем не менее, я думаю, что в этом контексте вы должны смотреть на две важные вещи, потому что в какой-то момент вам может потребоваться ручное распределение.

RAII-Идиома (Инициализация ресурсов)

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

Умные указатели

В этом случае это также может быть вариантом для вас, если вы хотите внести минимально возможные изменения. В c++ вы всегда должны использовать интеллектуальный указатель, если вы можете это сделать (что почти всегда). В новом стандарте есть два типа интеллектуальных указателей: уникальный указатель и общий указатель. Оба заслуживают долгого объяснения, но я отложу это (уникальный указатель, общий указатель). Было бы разумно читать те, кто даже за пределами определений, которые я связывал, потому что эти два очень мощные. Уникальный указатель позволяет иметь указатель, который не может быть скопирован, и который будет обрабатывать освобождение объекта, на который он указывает, когда он выходит из области видимости. Общий указатель хранит счетчик ссылок и освобождает объект, на который он указывает, как только счет достигает 0. В сочетании с RAII эти указатели обычно позволят вам писать код без явного распределения вручную, что обычно требуется в c++. Если вы не можете использовать интеллектуальные указатели из c++ 11, вам следует подумать о повышении интеллектуальных указателей.

1

Blockquote Я всегда кодировал в C, и я действительно начинаю делать C++

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

Какими бы ни были cups, похоже, что ваш класс State содержит два вектора.

class State
{
     // note these members are private

   std::vector< int > maxLiters_;
   std::vector< int > nowLiters_;

 public:
   explicit State( size_t cups )
         : maxLiters_( cups )
         ,  nowLiters_( cups )
   {
   }

   std::vector<int> const& maxLiters() const
  {
    return maxLiters_;
   }

   std::vector< int > const& nowLiters() const
  {
      return nowLiters ;
  }
};

int main()
{
    State state( 6 );

   // whatever
}

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

C++ - это другой язык, и вам нужно научиться его использовать и не думать о том, как вы его реализуете на C (или Java, С#, Python, Perl или любой другой язык).

Кстати, с вашей реализацией (даже если C если вы удаляете ссылки и т.д.) У вас возникла проблема, если первый malloc преуспеет, а второй - не удался. Вы знаете, что на самом деле не удалось? Знаете ли вы, free первый или нет?

Вы можете позаботиться об этом, установив оба указателя на nullptr, прежде чем пытаться выделить что-либо, но вы этого не сделали.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню