Зачем нужен этот конструктор копирования

#include <iostream>
#include <vector>

using namespace std;

class A {
private:
int number;
public:
A() {number = 0;}
A(int nr) {number = nr;}
//A(const A& rhand) {this->number = rhand.number;}
int get_number() const {return this->number;}
A& operator=(const A& rhand) {
this->number = rhand.number;
return (*this);
}
};

class B {
private:
vector<A*>* content;
vector<A*>::iterator currA;
bool currA_valid;
public:
B() {
content = new vector<A*>;
currA = content->begin();
currA_valid = false;
}
void push_A(A* nA) {content->push_back(nA); currA_valid = false;}
void push_A(A nA) {content->push_back(&nA); currA_valid = false;}
A get_A() {
if(!currA_valid) {
currA = content->begin();
if(!content->empty()) {
currA_valid = true;
}
}
if(currA == content->end() || this->content->empty()) {
currA = content->begin();
return A();
}
else {
A result(**currA);
++currA;
return result;
}
}
};

int main()
{
B container;

A* a1 = new A(1);
cout << a1->get_number() << endl;
A a2(2);
cout << a2.get_number() << endl;

container.push_A(a1);
container.push_A(a2);A tmp;
while((tmp = container.get_A()).get_number() != 0)
cout << "Inhalt tmp: " << tmp.get_number() << endl;

return 0;
}

Недавно я столкнулся с проблемой, которую превратил в этот фрагмент кода.

По сути класс B является контейнером для объектов класса A.

В реальном коде A намного больше, и один и тот же объект типа A может появляться в контейнере несколько раз, поэтому для экономии места B сохраняет только указатели на A.

Функции B :: push подают объекты типа A в контейнер.

Они перегружены для указателей и значений А.

Цикл while в конце основной функции — это то, что я хотел (вроде как потоковый оператор используется с объектами iostream).

Итератор «currA» в B отслеживает, какой элемент был последний раз возвращен вызовом функции B :: get_A (), поэтому последовательные вызовы этой функции возвращают все A-объекты в B :: content, пока не будет достигнут конец. В этом случае внутренний итератор сбрасывается, и возвращается объект A с внутренним недействительным флагом (в этом случае для простоты A :: число равно 0).

Вывод этой программы может выглядеть так:

1
2
Inhalt tmp: 1 //content of a1
Inhalt tmp: 4620996 //content of a2

Основная функция создает два объекта A a1 (1) и A * a2 (2).

Для проверки A :: get_number () отображаются оба их внутренних значения. Оба работают как положено. Но после того, как мы загрузили их обоих в контейнер и снова извлекли их из него, правильно отображается только a1. Содержание a2 показывает только некоторые случайные числа.

Сначала я подумал, что допустил ошибку с некоторыми указателями, но это доказывает, что проблема исправлена, если объявить и определить конструктор копирования для класса A следующим образом:

A(const A& rhand) {this->number = rhand.number;}

Насколько я понял, конструктор копирования будет неявно определяться компилятором c ++, если он не предоставлен, и рекомендуется его реализовывать, когда у класса есть указатели в качестве членов, чтобы избежать мелких копий. Но в этом случае A имеет только int.

Я также попытался упростить код, избавившись от B :: get_A () и получив содержимое контейнера другими способами. Проблема исчезла даже без реализации конструктора по умолчанию.
Итак, вот мои вопросы:

1.) Разве конструктор копирования, который определяет компилятор, не похож на тот, который я предоставил?
А также
2.) Что конструктор копирования вообще имеет отношение к реальной проблеме? Как реализация конструктора копирования решает проблему?

3

Решение

void push_A(A nA) {content->push_back(&nA); currA_valid = false;}

Здесь вы принимаете A объект по значению. Этот объект является локальным для функции. Когда функция возвращается, объект больше не существует, поэтому ваш вектор остается с недопустимым указателем.

Ваша реализация конструктора копирования не имела ничего общего с решением проблемы, это было просто совпадение.

2

Другие решения

Да, если конструктор копирования не предоставлен, компилятор записывает его. Но если вы определяете свой собственный оператор присваивания или деструктор, вам, вероятно, следует определить собственную реализацию copy-ctor.

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

Это известно какПравило трех».

0

По вопросам рекламы [email protected]