Копирование объектов со всеми элементами в C ++!(Конструкторы и назначение, лучшая практика?) - PullRequest
0 голосов
/ 22 октября 2018

Я перерыл SO и многое узнал о конструкторах по умолчанию, конструкторах копирования, назначении объектов, умных указателях, мелком / глубоком копировании и их взаимосвязях с динамическим выделением памяти (например, This , This , , что и ...).Тем не менее, я все еще не уверен, что могу сделать вывод о том, что лучше всего обрабатывать копирование таких объектов, как векторы (или списки).

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

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

Какова лучшая практика для простых маленьких объектов, таких как в следующем коде?Элементы в векторах не копируются должным образом.(Я не удивлюсь, если сделаю здесь очень простую ошибку. Также, если это возможно, предпочтительнее не использовать raw / shared / smart указатели).

#include <iostream>
#include <vector>
using namespace std;

class A{
    public:
    int id;
    A(int id_):id(id_){}
    vector<A> childs;
};

int main()
{
    A a0(0), a1(1);

    a0.childs={a1}; //node0.childs.push_back(node1);
    a1.childs={a0}; //node1.childs.push_back(node0);

    cout << a0.childs.size() << endl; // 1
    cout << a1.childs.size() << endl; // 1
    cout << a0.childs[0].childs.size() << endl; // expecting 1 but 0
    //Probably since they're not pointing to the same address of memory
    //I was hoping vector handle this by itself (was told best practice in this case is to not manage resources yourself)

    return 0;
}

Ответы [ 2 ]

0 голосов
/ 22 октября 2018

Мне кажется, я понимаю, чего вы пытаетесь достичь, но если цель состоит в обучении, тогда я настоятельно рекомендую вам понять, почему то, чего вы ожидаете, не происходит.Прежде чем перейти к поиску «обходного пути» для достижения того, чего вы пытаетесь достичь.

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

struct A {
    int childCount = 0;
};

int main() {
    A a1;
    std::vector<A> vecA{a1};
    a1.childCount = 1;
    std::cout << vecA[0].childCount<< "\n"; // What do you expect here?
}

, что эквивалентно:

A a1;
A copyOfA1 = a1;
a1.childCount= 1;
std::cout << copyOfA1.childCount << "\n"; // What do you expect here?

, что эквивалентно:

int a1 = 0;
int copyOfA1 = a1;
a1 = 1;
std::cout << copyOfA1 << "\n";  // What about here?

a0 содержит отдельную копию из a1, а не ссылку на a1, поэтому, если вы сделаете изменение в оригинале a1, копия a1, хранящаяся в a0, не будетизменить.

РЕДАКТИРОВАТЬ : Что касается того, как достичь того, чего вы хотите достичь.Я предполагаю, что A не должен владеть своими детьми.Вы хотите, чтобы он содержал не принадлежащие ссылки на A s, которые хранятся в другом месте.К сожалению, std::vector не может содержать ссылку на C ++.std::vector может содержать необработанный указатель, но вы специально попросили не использовать необработанные указатели.

Альтернативой является std::reference_wrapper<A>, который немного напоминает ссылку на C ++.но присваивается, чтобы его можно было использовать в std::vector.Вы можете скрыть std::reference_wrapper, предоставив функцию-член для доступа к дочернему элементу по индексу:

#include <iostream>
#include <vector>
#include <functional>

struct A {
    int id;
    A(int id_):id(id_){}
    std::vector<std::reference_wrapper<A>> childs;
    A& at(size_t index) { return childs[index]; }
};

int main()
{
    A a0(0), a1(1);

    a0.childs={a1};
    a1.childs={a0};

    std::cout << a0.childs.size() << "\n";
    std::cout << a1.childs.size() << "\n";
    std::cout << a0.at(0).childs.size() << "\n";
}

Демонстрационная версия .

Но, чтобы быть понятным,std::reference_wrapper - это просто оболочка необработанного указателя, вы должны убедиться, что объект, на который он указывает, все еще жив.

Edit2 : По запросу, вотверсия, которая использует вектор необработанных указателей:

#include <iostream>
#include <vector>

struct A {
    int id;
    A(int id_):id(id_){}
    std::vector<A*> childs;
    A& at(size_t index) { return *childs[index]; }
};

int main()
{
    A a0(0), a1(1);

    a0.childs={&a1};
    a1.childs={&a0};

    std::cout << a0.childs.size() << "\n";
    std::cout << a1.childs.size() << "\n";
    std::cout << a0.at(0).childs.size() << "\n";
}

Демо-версия .

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

0 голосов
/ 22 октября 2018
a0.childs={a1}; //a1.childs is empty here, so a0.childs will be empty too
a1.childs={a0}; //a0.childs contains one element here and so will a1.childs

Итак

 cout << a0.childs[0].childs.size() << endl; // This size will be 0

Но

cout << a1.childs[0].childs.size() << endl; // This will contain one element and the output shows the same.

Вывод:

a.exe
a0.childs.size():1
a1.childs.size():1
a0.childs[0].childs.size():0
a1.childs[0].childs.size():1
...