Схема родитель-ребенок - PullRequest
1 голос
/ 18 мая 2010

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

class MyClass
{
public:
    MyClass(const MyClass* ptr_parent): parent(parent){};
    ~MyClass(){ delete[] a_children; };
private:
    const MyClass* ptr_parent; // go to MyClass above
    MyClass* a_children; // go to MyClass below
    size_t sz_numChildren; // for iterating over a_children
}

(извините за встроенное кодирование, это только для краткости)

Уничтожит ли «Мастер MyClass» всех детей? Ни один ребенок не должен быть в состоянии убить своего родителя, потому что в моей главной программе будут указатели на уничтоженные объекты, правильно?

Почему вы можете спросить? Мне нужен способ «перебрать» все подкаталоги и найти все файлы на уровне, не зависящем от платформы. Создание этого дерева будет обрабатываться нативным API, остальное - нет. Это хорошая идея для начала?

Спасибо!

Ответы [ 5 ]

4 голосов
/ 18 мая 2010

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

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

  2. delete[] a_children не удалит дочерние элементы, он удалит массив, содержащий указатели на дочерние элементы. В деструкторе MyClass необходимо выполнить итерацию по массиву, удалив каждый дочерний указатель, а затем удалить массив. Фактически, было бы лучше использовать std::vector<MyClass*> для a_children вместо MyClass*, поэтому вам не нужно беспокоиться о (1). Но даже с вектором вам все равно придется повторять и удалять каждого потомка в деструкторе.

  3. Вашим детям нужно как-то «зарегистрироваться» на родительском объекте. Как вы это написали, дочерний объект не может сказать родительскому объекту, что он существует. По этой причине вы, вероятно, не сможете избежать передачи родительского указателя const .

Итак, в качестве примера:

class MyClass {

public:

  MyClass(MyClass* parent) 
    : m_parent(parent) 
  { 
    if (m_parent) m_parent->registerChild(this); 
  }

  ~MyClass() 
  {
    for (std::vector<MyClass*>::const_iterator i = m_children.begin(); 
         i != m_children.end(); ++i) 
    {
      delete *i;
    }
  }

  void registerChild(const MyClass* child) 
  {
    m_children.push_back(child);
  }

private:

  MyClass* m_parent;
  std::vector<const MyClass*> m_children;
};

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

2 голосов
/ 18 мая 2010

Да, если вы создаете детей с:

a_children = new Myclass[sz_numChildren];

удаление родителя в свою очередь удалит всех потомков.

Однако это не будет очень гибким по нескольким причинам.

  • Дочерние элементы должны быть созданы одновременно в непрерывном массиве. Вы не сможете реализовать такую ​​функцию, как addChild() очень легко.
  • Вы не сможете наследовать от MyClass и хранить производные типы как дочерние, поскольку они не сохраняются как указатели.

Я бы порекомендовал использовать вектор указателей на объекты MyClass для хранения дочерних элементов:

std::vector<MyClass*> a_children;

Тогда вы можете создавать детей по мере необходимости:

a_children.push_back(new MyClass(parent));

и удалите всех детей с помощью:

for(int i=0; i<a_children.size(); i++)
    delete a_children[i];

Фактический вектор указателей будет уничтожен автоматически.

0 голосов
/ 18 мая 2010

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

0 голосов
/ 18 мая 2010

Я не эксперт по C ++, но я не думаю, что кошерно delete[] детям, если они не получили new[].

См. C ++ FAQ по распределению памяти для получения более подробной информации.

Однако в остальном план выглядит здравым.

0 голосов
/ 18 мая 2010

Уничтожит ли «Мастер MyClass» всех детей?

Да, как у вас это закодировано, так и будет. Однако вы можете захотеть инициализировать a_children в NULL или в какой-либо области памяти, которую вы предварительно распределили.

Почему вы можете спросить? Мне нужен способ «перебрать» все подкаталоги и найти все файлы на платформе независимый уровень. Создание это дерево будет обрабатываться нативным API, остальные не будут. Это хороший идея начать с?

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

...