С++: реализация конструктора копирования и оператора присваивания копии
После чтения о конструкторах копий и операциях присваивания копий в С++ я попытался создать простой пример. Хотя приведенный ниже фрагмент, по-видимому, работает, я не уверен, правильно ли я использую конструктор копирования и оператор назначения копирования. Не могли бы вы указать, есть ли какие-либо ошибки/улучшения или лучший пример для понимания соответствующих концепций.
class Foobase
{
int bInt;
public:
Foobase() {}
Foobase(int b) { bInt = b;}
int GetValue() { return bInt;}
int SetValue(const int& val) { bInt = val; }
};
class Foobar
{
int var;
Foobase *base;
public:
Foobar(){}
Foobar(int v)
{
var = v;
base = new Foobase(v * -1);
}
//Copy constructor
Foobar(const Foobar& foo)
{
var = foo.var;
base = new Foobase(foo.GetBaseValue());
}
//Copy assignemnt operator
Foobar& operator= (const Foobar& other)
{
if (this != &other) // prevent self-assignment
{
var = other.var;
base = new Foobase(other.GetBaseValue());
}
return *this;
}
~Foobar()
{
delete base;
}
void SetValue(int val)
{
var = val;
}
void SetBaseValue(const int& val)
{
base->SetValue(val);
}
int GetBaseValue() const
{
return(base->GetValue());
}
void Print()
{
cout<<"Foobar Value: "<<var<<endl;
cout<<"Foobase Value: "<<base->GetValue()<<endl;
}
};
int main()
{
Foobar f(10);
Foobar g(f); //calls copy constructor
Foobar h = f; //calls copy constructor
Foobar i;
i = f;
f.SetBaseValue(12);
f.SetValue(2);
Foobar j = f = z; //copy constructor for j but assignment operator for f
z.SetBaseValue(777);
z.SetValue(77);
return 1;
}
Ответы
Ответ 1
Оператор присваивания копий выполняется неправильно. Объекту, назначаемому для утечки объекта, его base
указывает на.
Ваш конструктор по умолчанию также неверен: он оставляет обе строки base
и var
неинициализированными, поэтому нет способа узнать, является ли он действительным и в деструкторе, когда вы вызываете delete base;
, Bad Things Happen.
Самый простой способ реализовать конструктор копирования и оператора присваивания копий и знать, что вы сделали это правильно, - это использовать идиому копирования и свопинга.
Ответ 2
Только Foobar
требуется собственный конструктор копирования, оператор присваивания и деструктор. Foobase
не нужен, потому что поведение по умолчанию, которое дает компилятор, достаточно хорошо.
В случае Foobar
у вас есть утечка в операторе присваивания. Вы можете легко исправить это, освободив объект перед его распределением, и это должно быть достаточно хорошим. Но если вы добавите второй элемент указателя в Foobar
, вы увидите, что когда все усложняется. Теперь, если у вас есть исключение, выделяя второй указатель, вам нужно правильно очистить первый указатель, который вы выделили, чтобы избежать повреждения или утечки. И все становится сложнее, чем полиномиально, поскольку вы добавляете больше элементов указателя.
Вместо этого вы хотите реализовать оператор присваивания в терминах конструктора копирования. Затем вы должны реализовать экземпляр-конструктор с точки зрения неперебрасывающей функции обмена. Подробнее читайте о идиоме копирования и свопинга.
Кроме того, конструктор по умолчанию Foobar
не выполняет инициализацию элементов по умолчанию. Это плохо, потому что это не то, чего ожидал пользователь. Указатель-член указывает на произвольный адрес, а int имеет произвольное значение. Теперь, если вы используете объект, созданный конструктором, вы очень близки к Undefined Behavior Land.
Ответ 3
У меня для вас очень простой патч:
class Foobar
{
int var;
std::unique_ptr<FooBase> base;
...
Это должно заставить вас начать.
В нижней строке:
- Не вызывайте
delete
в свой код (эксперты видят пункт 2).
- Не вызывайте
delete
в свой код (вы знаете лучше...)