Russian Qt Forum

Программирование => С/C++ => Тема начата: Blackwanderer от Март 26, 2012, 16:50



Название: Стоит ли запрещать конструктор копирования и оператор присваивания?
Отправлено: Blackwanderer от Март 26, 2012, 16:50
Одна из рекомендаций в Google Code Style (http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Copy_Constructors) звучит следующим образом:
Код:
Provide a copy constructor and assignment operator only when necessary. Otherwise, disable them with DISALLOW_COPY_AND_ASSIGN.
Обоснование приводится следующее:
Код:
Implicit copying of objects in C++ is a rich source of bugs and of performance problems.
It also reduces readability, as it becomes hard to track which objects are being passed
around by value as opposed to by reference, and therefore where changes to an object are reflected.
Как вы считаете, действительно ли это стоящая рекомендация и следует её придерживаться, или это просто преверженность привычному C-стилю и страх перед новым, мол какая-то это новая неведамая фигня, давайте не будём её использовать?

И небольшой частный случай, привёдший к такому вопросу. Есть класс ConstMatrix (матрица, элементы которой нельзя изменять) и Matrix (просто матрица). Matrix наследует от ConstMatrix. Оба типа матриц нужно транспонировать. Результат транспонирования всегда будет ConstMatrix. Важно заметить, что сложность копирования матрицы при такой архитектуре стремится к O(1). Соответственно вижу два подхода к решению этой задачи:
  • Запрещаем конструктор копирования и присваивания
Код
C++ (Qt)
void ConstMatrix::transpose(ConstMatrix &transposed) const
{
// Проверяем в runtime действительно ли transposed - объект класса ConstMatrix, а не объект дочернего класса.
// Если это объект дочернего класса - получаем потенциальную проблему безопасности.
transposed=...
}
 
void someFunction(const Matrix &A)
{
ConstMatrix transposed;
A.transpose(transposed);
....
}
 
  • Разрешаем конструктор копирования и присваивания
Код
C++ (Qt)
ConstMatrix ConstMatrix::transposed() const
{
Const Matrix transposed;
...
return transposed;
}
 
void someFunction(const Matrix &A)
{
ConstMatrix transposed = A.transposed();
....
}
 

    Мне больше нравится второй вариант потому что
    1. Отсев ошибок на этапе компиляции предпочтительней отсева ошибок на этапе выполнения.
    2. Минимизируется время между объявлением и инициализацией переменной transposed
    3. Такой код легче читается, более понятен
    НО! Может быть есть какие-то подводные камни из-за разрешения присваивания/копирования (лично я-то ничего плохого/опасного в этом не вижу)?


    Название: Re: Стоит ли запрещать конструктор копирования и оператор присваивания?
    Отправлено: Пантер от Март 26, 2012, 16:54
    Или реализовываешь свой вариант, или запрещаешь, чтобы не огрести проблем со сгенерированными компилятором.


    Название: Re: Стоит ли запрещать конструктор копирования и оператор присваивания?
    Отправлено: Igors от Март 26, 2012, 21:09
    Как вы считаете, действительно ли это стоящая рекомендация и следует её придерживаться, или это просто преверженность привычному C-стилю и страх перед новым, мол какая-то это новая неведамая фигня, давайте не будём её использовать?
    Мое мнение - безусловно стоящая рекомендация которой стоит придерживаться. Может быть та или иная "модернуха" которая выглядит заманчиво/элегантно, но после применения я лично часто убеждался что выигрыш не так уж велик, а вот ненужных раздумий выплывает много. Короче - действовать по классике никогда не плохо, на то она и классика.

    И небольшой частный случай, привёдший к такому вопросу. Есть класс ConstMatrix (матрица, элементы которой нельзя изменять) и Matrix (просто матрица). Matrix наследует от ConstMatrix. Оба типа матриц нужно транспонировать. Результат транспонирования всегда будет ConstMatrix. Важно заметить, что сложность копирования матрицы при такой архитектуре стремится к O(1). Соответственно вижу два подхода к решению этой задачи:
    Ну далее в приведенном Вами коде вы (борзо) возвращаете по значению, поэтому надо ставить вопрос раньше - хотите ли Вы возвращать по значению? Для простых структур это нормально, напр
    Код
    C++ (Qt)
    QRect R = ...
     
    Здесь глупо каждый раз возиться с QRect по ссылке/указателю. Но если Ваша Matrix - потенциально большие данные - то ну его нафиг тот модерн, тут "подальше положишь - поближе возьмешь". Как правило отслеживание "а не сделал ли я чего" займет куда больше времени и сил чем чуть более развесистый код

    Ну конечно об оптимизации компилятора все в курсе, об implicit sharing также, давайте не тратить на это время


    Название: Re: Стоит ли запрещать конструктор копирова&#
    Отправлено: Blackwanderer от Март 27, 2012, 04:21
    Ну далее в приведенном Вами коде вы (борзо) возвращаете по значению, поэтому надо ставить вопрос раньше - хотите ли Вы возвращать по значению?
    Я же написал, что сложность копирования стремится к O(1). Матрица неизменяемая, поэтому Shallow Copy.


    Название: Re: Стоит ли запрещать конструктор копирова&#
    Отправлено: Igors от Март 27, 2012, 10:20
    Я же написал, что сложность копирования стремится к O(1). Матрица неизменяемая, поэтому Shallow Copy.
    Но Вы же не написали что зарядили в класс матрицы implicit sharing (само оно ниоткуда не возьмется). Если так то вопрос "исперчен" - действуйте в духе Qt, по существу Вы приняли решение раньше. 


    Название: Re: Стоит ли запрещать конструктор копирования и оператор присваивания?
    Отправлено: Akon от Март 27, 2012, 13:46
    А вот запрещаете ли вы явно конструктор копирования и оператор присваивания в классах, наследуемых от классов, где эти операции уже запрещенны?
    Код:
    class MyObject : public QObject
    {
        Q_OBJECT
        Q_DISABLE_COPY(MyObject)  // пишите это или влом?
    }

    Если не запрещаете, то конструктор копирования и оператор присваивания формально будут доступны, но пользоваться ими будет нельзя, т.к. они недоступны в базовом классе. Результат - compile-time error.

    В Qt для QObject-типов пендюрят везде Q_DISABLE_COPY.


    Название: Re: Стоит ли запрещать конструктор копирова&#
    Отправлено: Blackwanderer от Март 27, 2012, 16:51
    А вот запрещаете ли вы явно конструктор копирования и оператор присваивания в классах, наследуемых от классов, где эти операции уже запрещенны?
    Ясен пень, что если запрещать, то везде надо :)