Скажем, у меня есть структура «s» с переменной-членом-указателем int «i». Я выделяю память в куче для i в конструкторе по умолчанию для s. Позже в другой части кода я передаю экземпляр s по значению некоторой функции. Я делаю здесь мелкую копию? Предположим, я не реализовал конструкторы копирования, операторы присваивания или что-то еще для s ... только конструктор по умолчанию.





Да, это мелкая копия. Теперь у вас есть две копии s (одна в вызывающей стороне, одна в стеке в качестве параметра), каждая из которых содержит указатель на тот же блок памяти.
У вас будет две копии структуры s, каждая из которых будет иметь свой собственный указатель i, но оба указателя i будут иметь одинаковое значение, указывающее на один и тот же адрес в памяти - так что да, это будет неглубокая копия.
В продолжение того, что сказал @ [don.neufeld.myopenid.com], это не только поверхностная копия, но и (на ваш выбор) утечка памяти или висячий указатель.
// memory leak (note that the pointer is never deleted)
class A
{
B *_b;
public:
A()
: _b(new B)
{
}
};
// dangling ptr (who deletes the instance?)
class A
{
B *_b;
public:
A()
... (same as above)
~A()
{
delete _b;
}
};
Чтобы решить эту проблему, существует несколько методов.
Всегда реализуйте конструктор копирования и оператор = в классах, которые используют указатели на необработанную память.
class A
{
B *_b;
public:
A()
... (same as above)
~A()
...
A(const A &rhs)
: _b(new B(rhs._b))
{
}
A &operator=(const A &rhs)
{
B *b=new B(rhs._b);
delete _b;
_b=b;
return *this;
};
Излишне говорить, что это большая проблема, и есть немало тонкостей, которые нужно исправить. Я даже не совсем уверен, что сделал это прямо здесь, и делал это несколько раз. Не забывайте, что вам нужно скопировать всех участников - если вы добавите новых позже, не забудьте добавить и их!
Сделайте конструктор копирования и operator = private в своем классе. Это решение «запереть дверь». Это просто и эффективно, но иногда чрезмерно защищает.
class A : public boost::noncopyable
{
...
};
Никогда не используйте необработанные указатели. Это просто и эффективно. Здесь есть много вариантов:
Пример:
// uses shared_ptr - note that you don't need a copy constructor or op= -
// shared_ptr uses reference counting so the _b instance is shared and only
// deleted when the last reference is gone - admire the simplicity!
// it is almost exactly the same as the "memory leak" version, but there is no leak
class A
{
boost::shared_ptr<B> _b;
public:
A()
: _b(new B)
{
}
};
Дох, хорошо, я исправил это. Вы понимаете, что я имею в виду, говоря, что это боль и трудно понять, тогда да
Лучший способ сделать конструктор копирования и operator = private - наследовать от boost :: noncopyable. Вы должны сделать это для каждого отдельного класса, если не знаете наверняка, что его можно скопировать.
С идиомой копирования и обмена действительно легко сделать это правильно (даже если это немного неэффективно)
Ваш оператор присваивания не безопасен для исключений. См. Недавний вопрос о безопасности копирования и исключений: stackoverflow.com/questions/214891/…