Разрушители и члены Malloc'd - PullRequest
       24

Разрушители и члены Malloc'd

3 голосов
/ 18 января 2012

Допустим, например, что у меня есть класс, который требует использования некоторого старого материала на C (например, pthreads или чего-то такого), поэтому по той или иной причине я закончил с вызовом malloc () в моемконструктор, вот так:

class Foo
{
  public:
    Foo()
    {
      someMutex = malloc(sizeof(pthread_mutex_t));
      pthread_mutex_init(someMutex);
      someString = new string("yay string!");
    }

  private:
    pthread_mutex_t * someMutex;
    string * someString;
}

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

Таким образом, возникает вопрос: даже если someMutex был назначен с malloc, а не командой C ++ new,неявно определенный деструктор все еще заботится об этом, или я должен это делать?

Кроме того, давайте просто разрешим еще один мой вопрос, поскольку он так тесно связан.В вышеприведенном классе, мне нужно явно определить деструктор для вызова delete на someString, или это позаботилось обо мне?

Ответы [ 5 ]

8 голосов
/ 18 января 2012

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

Неявно определенный деструктор уничтожает переменные-члены.Так, например, если у вас была переменная-член типа string, деструктор уничтожит эту переменную самостоятельно.Однако деструктор для указателя (например, string*) не предназначен: вы несете ответственность за уничтожение объекта, на который указывает указатель.

Вам также необходимо определить операции копирования для этого класса или вкак минимум подавить генерацию операций копирования по умолчанию, которые предоставляет вам компилятор.Зачем?Потому что по умолчанию операции копирования просто копируют каждую переменную-член.Так, если, например, вы должны были написать:

{
    Foo x;
    Foo y(x);
}   // Uh oh

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


Лучший вариант - вообще не использовать ручное выделение памяти.Скорее, вы должны сделать someString прямым членом класса (то есть объявить его string someString;) или использовать какой-нибудь умный указатель для управления его временем жизни (например, unique_ptr или shared_ptr).Точно так же вы должны использовать умный указатель с пользовательским средством удаления для управления временем жизни мьютекса, если ваш класс не является копируемым, и в этом случае вы можете сделать мьютекс прямым членом класса.

3 голосов
/ 18 января 2012

Да, вы должны определить деструктор и уничтожить ваши объекты (someMutex и someString).

Но, поскольку вы выделили someMutex с malloc, вы должны освободить его с free.

Старайтесь не смешивать их.

Помните:

  • выделено с malloc, освобождено с free
  • выделено с new, освобождено с delete
  • выделено с new[], освобождено с delete[]
2 голосов
/ 18 января 2012

Вместо хранения указателя на string в вашем классе, я бы просто сохранил экземпляр string в качестве члена данных (используя «семантику стека»).

Более того, вместо того, чтобы хранить указатель на «raw» pthread_mutex_t, я бы определил класс C ++, чтобы обернуть этот ресурс pthread_mutex_t, используя RAII (создавая pthread_mutex_t в его конструкторе и уничтожив его в деструкторе), а затем я сохраню экземпляр этого класса C ++ в качестве члена данных Foo.

//
// C++ RAII wrapper on raw C pthread_mutex_t resource.
//
class PThreadMutex
{
public:

  // Creates a pthread_mutex_t.
  PThreadMutex()
  {
    pthread_mutex_init(&m_mutex, ...);
    // Check for errors, and throw exceptions on errors
  }

  // Destroys a pthread_mutex_t
  ~PThreadMutex()
  {
    pthread_mutex_destroy(&m_mutex);
  }


  // Other member functions
  // ...


  // May define move constructor and move assignment operator for C++11
  // ...

private:
  pthread_mutex_t m_mutex;
};



class Foo
{
public:

  Foo()
    : m_someString("yay string!")
     // m_someMutex initialized by its default constructor
  {
  }


  ~Foo()
  {
     // Nothing to do: C++ compiler will call the destructors
     // of class data members, releasing their associated resources.
  }


private:
  //
  // Class "building blocks":
  //
  PThreadMutex m_someMutex;
  string m_someString;
};

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

Как правило, каждый «сырой» ресурс C (pthread_mutex_t, FILE * и т. Д.) Должен быть заключен в класс C ++ с использованием RAII и экземпляров этих классов (как будто они являются своего рода «строительными блоками»). должны использоваться в качестве членов данных других классов. Это помогает упростить ваш код (а также написать код, безопасный для исключений); если вы воспользуетесь этим шаблоном, вы сможете достичь хорошего уровня безопасности и компоновки кода.

1 голос
/ 18 января 2012

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

И еще одна вещь. После выделения памяти с помощью malloc вы должны освободить ее с помощью free().

0 голосов
/ 18 января 2012

Нужно ли вам определять деструктор или нет, зависит от того, СОБСТВЕННЫЙ ли текущий объект СОБСТВЕННЫМ созданным объектам, или он просто создает их для управления каким-либо другим объектом, соответственно.

Когда вы выделяете кучу памяти с malloc(), вы должны освободить ее с free(). Когда вы создаете объекты с new, вы должны удалить их с delete. Когда вы создаете массив с new[], вы должны удалить его с delete[].

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

Другой вариант - использование «умного указателя» (http://en.wikipedia.org/wiki/Smart_pointer),, который фактически удалит остроконечный объект при удалении текущего объекта (или выходе из области видимости).

...