Переопределение оператора = в C ++ - PullRequest
0 голосов
/ 04 февраля 2011

Моя задача - реализовать метод, который переопределяет оператор =. Я кое-что написал, но, честно говоря, я понятия не имею, что я делаю. Может кто-нибудь объяснить, в чем смысл этого переопределения (не в общем, я понимаю, но только в этом случае я немного запутался)? Что должен сделать мой метод? Где моя логика терпит неудачу в моем текущем коде?

scene.cpp:70: error: no match for ‘operator=’ in ‘*(((Image*)(((long unsigned int)i) * 80ul)) + newArray) = *(((Scene*)this)->Scene::images + ((Image**)(((long unsigned int)i) * 8ul)))’

Ответы [ 6 ]

1 голос
/ 04 февраля 2011

Что ваш (внутренний) код делает на английском языке:

//allocate new memory and copy
images = new Image*[source.maximum];

Устанавливает images для вновь распределенного массива source.maximum неинициализированных Image указателей. Все, на что указывал images, потеряно.

    Scene(source);

Это создает новый временный Scene объект и затем выбрасывает его. он не «повторно вызывает» конструктор на this.

    //deallocate old memory
    delete *source;

Это, если бы это работало, разыменовало бы source (что является const Scene&, так что это работает, только если определено T* Scene::operator *(void), где T - некоторый тип) и удаляло бы указанный T объект. * * тысяча двадцать-один

    //assign
    source=images;

Попытка скопировать images поверх source, что не должно происходить, поскольку source равно const. После создания ссылка не может быть изменена для ссылки на другой объект.

    this->maximum=images.maximum;

Это не работает. images - это Image**, в котором нет поля maximum. Кроме того, this-> является избыточным.

ОБНОВЛЕНИЕ: Относительно новой версии:

Во-первых, вам не нужно говорить this-> везде.

for (int i=0;i<source.maximum;i++)
    this->images[i]=source->images[i];

Проблема здесь в том, что source является ссылкой, а не указателем, поэтому вы должны использовать source.images[i] вместо source->images[i].

Предполагая, что это исправлено, теперь проблема в том, что на объекты изображения указывают как текущий объект, так и source. Если какой-либо объект освобождает память (delete images[i]; images[i] = 0; или аналогичный), указатель на другой объект становится недействительным Если изображения никогда не удаляются, , тогда этот код подходит.

Если, однако, вы хотите, чтобы этот объект имел свои собственные копии изображений, вам нужно проделать дополнительную работу:

if(this != &source)
{
    // Delete old images.
    for(int i = 0; i < maximum; i++)
        delete images[i];
    delete[] images;

    // Copy new images.
    maximum = source.maximum;
    images = new Image*[maximum];
    for(int i = 0; i < maximum; i++)
        images[i] = new Image(*(source.images[i]));
}

Предполагается, что у вас есть конструктор копирования для Image (Image::Image(const Image&);).

Наконец, Scene должен иметь конструктор копирования, который работает аналогично этому, за исключением того, что ему не нужно удалять старые вещи. Если вы не делаете копии изображений, используйте:

Scene::Scene(const Scene& original): maximum(original.maximum)
{
    images = new Image*[maximum];
    for(size_t i = 0; i < maximum; i++)
        images[i] = source.images[i];
}

Если вы делаете копируете изображения, используйте:

Scene::Scene(const Scene& original): maximum(original.maximum)
{
    images = new Image*[maximum];
    for(size_t i = 0; i < maximum; i++)
        images[i] = new Image(*(source.images[i]));
}

В обоих случаях не забудьте добавить Scene(const Scene& original); к определению класса.

1 голос
/ 04 февраля 2011

Очень сложно реализовать конструктор копирования или оператор присваивания в классе, который управляет памятью, и сохранить ваше исключение кода безопасным. Безопасность исключений означает, что при возникновении исключения в любой точке вашей программы память, управляемая вручную, по-прежнему очищается правильно. Несколько фраз возникли, чтобы помочь с этим:

Основной арендатор RAII заключается в том, что если вам нужно управлять памятью (а в последнее время это происходит гораздо чаще), оберните обработку памяти в объект, который выделяет только один фрагмент памяти на этапе строительства, и освобождают эту память при уничтожении. .

Копирование и замена - это конкретное руководство для реализации нетривиального оператора присваивания, когда вам приходится иметь дело с распределением памяти. Это именно тот сценарий, который вы пытаетесь решить.

Я рекомендую вам прочитать немного об обоих, прежде чем продолжать пытаться писать код, который управляет памятью, или вы, вероятно, порвете свои волосы, пытаясь уничтожить все ваши ошибки.

Альтернативой реализации собственных идиом является использование кода, подобного std::vector и tr1::shared_ptr, для управления вашей памятью. Множество людей, знакомых с C ++ и странностями управления наизнанку, используют их на регулярной основе. Вы можете очень часто сойти с рук только с этими.

0 голосов
/ 04 февраля 2011

Существует каноническое решение для реализации нетривиального оператора присваивания:

Scene const & Scene::operator=(Scene s) { swap(s); return *this; }
void Scene::swap(Scene & s) /* nothrow */ {
    Image *temp_images = images; images = s.images; s.images = temp_images;
    int temp_maximum = maximum; maximum = s.maximum; s.maximum = temp_maximum;
}

Это полностью безопасное исключение, так как он использует конструктор копирования для создания временной копии источника перед заменойэто в (используя обмен без броска), чтобы заменить цель.Если какая-либо часть копии будет неправильной, источник и цель останутся прежними.

Единственное, что он не делает, - это оптимизирует самостоятельное назначение.Однако при правильном назначении он работает правильно, и я, как правило, не пытаюсь оптимизировать то, что не должно происходить (а на практике это случается редко).

Другой вариант - использоватьболее традиционный тип параметра:

Scene const & Scene::operator=(Scene const & s) {
    Scene temp(s);
    swap(temp);
    return *this;
}

Однако, помимо того, что эта версия более многословна, она потенциально менее эффективна, поскольку гарантирует, что копия сделана, тогда как версия с передачей по значению может исключитьcopy, если параметр, переданный в s, является значением r.

0 голосов
/ 04 февраля 2011

В вашем коде есть несколько ошибок.

  1. Не delete source, вы передаете его как ссылку на const, так что оставьте его в покое!
  2. Не изменяйте источник. Это const, так что вы не можете.
  3. Не выделять новую память для this->images без delete[] images первого.
  4. Сначала нужно понять разницу между ссылками и указателями.
  5. Вы должны понять, что такое const.
  6. Вы не можете назначить images на source по разным причинам. Во-первых, вы не должны (не можете) назначать source. Другая причина в том, что, если Scene не является Image, вы не можете назначить Image указателю Scene.
0 голосов
/ 04 февраля 2011

Не совсем уверен, что вы хотите, чтобы он делал. Но есть много недостатков

Как правило, оператор = берет копию оригинала без каких-либо изменений в оригинале.

Похоже, вы хотите передать право собственности на ваши изображения с одного объекта на другой.

удалить * источник просто ОПАСЕН, поскольку не является / не должен быть владельцем. source может легко быть переменной стека.

источник! = Изображения, поэтому назначение изображений источнику просто убьет вещи

Хранение изображений в векторе будет обрабатывать выделение памяти, а также копирование всех изображений для вас.

0 голосов
/ 04 февраля 2011

Переопределение оператора похоже на перегрузку функции; Вы можете сделать копию через = op.


Scene A;
Scene B;

A.do_things();
B = A;

Если вы не перегружали оператор =; B = A сделал бы B указателем на тот же объект, что и A (поскольку переменные экземпляра класса в C являются указателями).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...