#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.) Что конструктор копирования вообще имеет отношение к реальной проблеме? Как реализация конструктора копирования решает проблему?
void push_A(A nA) {content->push_back(&nA); currA_valid = false;}
Здесь вы принимаете A
объект по значению. Этот объект является локальным для функции. Когда функция возвращается, объект больше не существует, поэтому ваш вектор остается с недопустимым указателем.
Ваша реализация конструктора копирования не имела ничего общего с решением проблемы, это было просто совпадение.
Да, если конструктор копирования не предоставлен, компилятор записывает его. Но если вы определяете свой собственный оператор присваивания или деструктор, вам, вероятно, следует определить собственную реализацию copy-ctor.
В общем, если вы пишете реализацию одной из этих конструкций (Copy-ctor, оператор присвоения и / или деструктор), вы должны написать свою собственную реализацию других.
Это известно какПравило трех».