Это - невоспитанность для вызова оператора присваивания по умолчанию от конструктора копии?

XNA должен предназначаться для XBox 360 и JIT в.NET, Компактная Платформа не так сложна как ее настольный дубликат..NET CF JIT'er не встроит методы свойства.

13
задан Marc 7 October 2009 в 21:32
поделиться

6 ответов

Как указывает brone, вам лучше реализовать присваивание с точки зрения создания копии. Я предпочитаю альтернативную идиому его:

T& T::operator=(T t) {
    swap(*this, t);
    return *this;
}

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

Во-первых, параметр t намеренно передается по значению, так что конструктор копирования будет вызываться ( наиболее времени ), и мы можем изменять его по своему усмотрению, не влияя на исходное значение. Используя const T & не будет компилироваться, а T & вызовет некоторое неожиданное поведение, изменив значение, полученное от назначенного.

Этот метод также требует, чтобы swap был специализирован для типа в способ, который не использует оператор присваивания типа (как это делает std :: swap ), или это вызовет бесконечную рекурсию. Будьте осторожны с любыми случайными , использующими std :: swap или с использованием пространства имен std , поскольку они втянут std :: swap в область видимости и вызовут проблемы, если вы этого не сделали. t specialize swap для T . Разрешение перегрузки и ADL обеспечат использование правильной версии подкачки, если вы ее определили.

Есть несколько способов определить своп для типа. Первый метод использует функцию-член swap для выполнения фактической работы и делегирует ей специализацию swap , например:

class T {
public:
    // ....
    void swap(T&) { ... }
};

void swap(T& a, T& b) { a.swap(b); }

Это довольно часто встречается в стандартной библиотеке; std :: vector , например, имеет такой способ подкачки. Если у вас есть функция-член swap , вы можете просто вызвать ее непосредственно из оператора присваивания и избежать проблем с поиском функции.

Другой способ - объявить swap как дружественную функцию. и пусть он выполняет всю работу:

class T {
    // ....
    friend void swap(T& a, T& b);
};

void swap(T& a, T& b) { ... }

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

14
ответ дан 1 December 2019 в 22:39
поделиться

В вашей версии конструктора копирования члены сначала конструируются по умолчанию, а затем назначаются.
Для целочисленных типов это не имеет значения, но если у вас есть нетривиальные члены, такие как std :: string s, это лишние накладные расходы.

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

4
ответ дан 1 December 2019 в 22:39
поделиться

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

Намного лучше было бы использовать идиому копирования и обмена и указать, какие члены не должны копироваться в конструкторе копирования. У вас все еще есть одно место, где выражается поведение «не копировать этот член», но теперь ваш оператор присваивания и конструктор копирования согласованы.

class A
{
public:

    A() : a(), b(), c(), d() {}

    A(const A& other)
        : a(other.a)
        , b(other.b)
        , c() // c isn't copied!
        , d(other.d)

    A& operator=(const A& other)
    {
        A tmp(other); // doesn't copy other.c
        swap(tmp);
        return *this;
    }

    void Swap(A& other)
    {
        using std::swap;
        swap(a, other.a);
        swap(b, other.b);
        swap(c, other.c); // see note
        swap(d, other.d);
    }

private:
    // ...
};

Примечание: в функции-члене swap я заменил элемент c . Для целей использования в операторе присваивания это сохраняет поведение, соответствующее поведению конструктора копирования: он повторно инициализирует член c . Если вы оставите функцию swap общедоступной или предоставите доступ к ней через бесплатную функцию swap , вы должны убедиться, что это поведение подходит для других применений swap.

2
ответ дан 1 December 2019 в 22:39
поделиться

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

Если я правильно помню, C ++ 0x позволит вам это сделать :

private:
    A &operator=(const A &) = default;

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

1
ответ дан 1 December 2019 в 22:39
поделиться

Я бы назвал это дурным тоном не потому, что вы дважды назначаете все свои объекты, а потому, что, по моему опыту, часто полагаться на конструктор копирования / оператор присваивания по умолчанию для определенного набора функций является плохим тоном. Поскольку их нигде нет в источнике, трудно сказать, зависит ли от них желаемое поведение. Например, что, если кто-то через год захочет добавить в ваш класс вектор строк? У вас больше нет простых старых типов данных, и специалисту по сопровождению было бы очень трудно знать, что они что-то ломали.

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

0
ответ дан 1 December 2019 в 22:39
поделиться

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

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

Если вам по какой-то причине необходимо предоставить конструктор неглубокого копирования по умолчанию, то в C ++ 0X есть

 X(const X&) = default;

Но я не думаю, что существует идиома для странной семантики. В этом случае использование присваивания вместо инициализации обходится дешево (поскольку оставление ints неинициализированными не означает

0
ответ дан 1 December 2019 в 22:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: