Potrzebuję operacji na listach w C++. Z pozoru zadanie może wydawać się trywialne, ale po pierwsze, nie dołączam pojedynczego obiektu, tylko na boku tworzę grupę na przykład drzewo z 5 elementów a potem podłączam po d któryś węzeł. Po drugie, musi być klonowanie - nie chcę aby zmiana w jednym miejscu zmieniała w innym gdy dwa razy podczepię element w dwóch różnych miejscach. Poza tym dziedziczenie z polimorfizmem oraz operacja, która powoduje trudności - zastąpienie poddrzewka innym. Na początku, zastępowanie drzewka określonego typu - tu były trudności bo nie mogę dla węzła a wykonać zmiany tego węzła, mogę zmieniać jego dane ale nie jego adres. Powinno to byc drzewko , lista dzieci i parent, oraz głębokość, czyli jak dodaję, to również dzieci aktualizują głębokość i parenta do nowych sklonowanych elementów.
Zadasz jakieś pytanie, czy mamy tylko poklepać Cię po ramieniu?
Jak zrobić coś takiego: chcę wymienić w drzewku element na poddrzewko, ale nie mogę ponieważ gdy dla elementu a wołam a.replace(subtree), wtedy w a mogę zmienić dane, ale nie mogę zmienić samego wskazania na a.
Co to znaczy element na poddrzewko? Chcesz zmienić węzeł na poddrzewo, a reszta nie zmieniona, tylko wskazuje na nowy element?
Tak, aby zgadzały się linki ojciec - dzieci.
Piszę od początku,
class Node
{
vector<Node*> childs;
Node* parent = nullptr;
int level = 0;
najprościej jak można
void Add(Node* node)
{
childs.push_back(node);
node->parent = this;
node->level = level+1;
}
Teraz chcę zamienić to na shared_ptr<Node>, niby nic trudnego ale jak zamienić node->parent = this ? W C++ this to goły wskaźnik, robiłem coś takiego dając do procedury shared_ptr<Node> th:
void Add(Node* node, shared_ptr<Node> th)
Następna sprawa: jak uniknąć cykli? Jest:
int main()
{
Node *root;
root = new Node("1");
Node *node = new Node("2");
root->Add(node);
node->Add(root); //<---to spowoduje cykl
root->print();
return 0;
}
Można patrzeć na level ,ale jak dokładnie
Następnie - klonowanie.
Co do shared: to nie wiedziałem że jest shared_from_this()
, które można wołać gdy przedziedziczy się własną klasę z std::enable_shared_from_this<NazwaMojejKlasy>
Tylko że shared from this będzie niepotrzebny z powodu wycieków pamięci dla cyklicznych zależności. Lepsze weak albo po prostu wskaźnik.(były problemy z ustawieniem weak na nullptr)
Wersja która dodaje drzewo, i robi erase zerując dzieci ale jak usunąć ten węzeł?
#include <memory>
#include <string>
#include <vector>
#include <assert.h>
using namespace std;
class Node
{
vector<shared_ptr<Node>> childs;
Node* parent = nullptr;//not shared_ptr! because of memory leaks of circular dependency
int level = 0;
public:
string name;
Node(string name)
{
this->name = name;
}
~Node()
{
printf("delete %s\n",name.c_str());
}
shared_ptr<Node> clone()
{
shared_ptr<Node> result = make_shared<Node>(name+"a");
return result;
}
void erase()
{
printf("erase from %s\n", name.c_str());
childs.clear();
}
void Add(shared_ptr<Node> node)
{
shared_ptr<Node> clone = node->clone();
childs.push_back(clone);
clone->parent = this;
clone->level = level+1;
for (size_t i = 0; i<node->childs.size(); i++)
clone->Add(node->childs[i]);
}
shared_ptr<Node>& at(int index)
{
return childs[index];
}
void print()
{
for (int i = 0; i<level; i++)
printf(" ");
printf("%s->",name.c_str());
if (parent) printf("%s", parent->name.c_str());
printf("\n");
for (size_t i=0; i<childs.size(); i++)
childs[i]->print();
}
};
int main()
{
shared_ptr<Node>root,rootB;
root = make_shared<Node>("1");
root->Add(make_shared<Node>("2"));
root->Add(make_shared<Node>("3"));
root->at(0)->Add(make_shared<Node>("4"));
root->at(0)->Add(make_shared<Node>("5"));
root->at(1)->Add(make_shared<Node>("6"));
root->at(1)->Add(make_shared<Node>("7"));
rootB = make_shared<Node>("1b");
rootB->Add(make_shared<Node>("2b"));
rootB->Add(make_shared<Node>("3b"));
rootB->at(0)->Add(make_shared<Node>("4b"));
rootB->at(0)->Add(make_shared<Node>("5b"));
rootB->at(1)->Add(make_shared<Node>("6b"));
rootB->at(1)->Add(make_shared<Node>("7b"));
// node->Add(root);
//root->print();printf("\n");
//rootB->print(); printf("\n");
root->at(0)->Add(rootB);
rootB = nullptr;
//rootB->at(0)->Add(rootB);//tu cykl?
root->print();
root->at(0)->erase();
root->print();
return 0;
}
Okazuje się, że nie tak jak powinno być zrobiłem klonowanie - teraz gdy podczepiam poddrzewo, wchodzi w głąb i klonuje w razie potrzeby po jednym obiekcie. A co gdy chcę podczepić w pewnym miejscu właśnie to drzewo, czyli roota? Więc powinien całe drzewo sklonować a potem podczepić. Jak to zrobić?