Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.73/11: Рейтинг темы: голосов - 11, средняя оценка - 4.73
 Аватар для GoldenId
142 / 143 / 64
Регистрация: 11.11.2010
Сообщений: 877
Записей в блоге: 10

Конструктор копирования и оператор присваивания - общая часть, выделять ли в отдельный метод

20.04.2017, 04:56. Показов 2071. Ответов 14
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Как лучше?

1
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// конструктор копирования
Fraction::Fraction( const Fraction& rhs )
{
    // инициализируем данными параметра функции и копируем значения
    init( rhs._intlen, rhs._fraclen );
    memcpy( _int, rhs._int, );
    memcpy( _frac, rhs._frac, );    
}
 
Fraction Fraction::operator = ( const Fraction& rhs )
{
    // брутфорс удаляем предыдущие массивы
    delete[] _int;
    delete[] _frac;
    // инициализируем данными параметра функции и копируем значения
    init( rhs._intlen, rhs._fraclen );
    memcpy( _int, rhs._int, );
    memcpy( _frac, rhs._frac, );    
}


2
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// конструктор копирования
Fraction::Fraction( const Fraction& rhs )
{
    copy( rhs );
}
 
Fraction Fraction::operator = ( const Fraction& rhs )
{
    // брутфорс удаляем предыдущие массивы
    delete[] _int;
    delete[] _frac;
    copy( rhs );    
}
 
void Fraction::copy( const Fraction& rhs )
{
    // инициализируем данными параметра функции и копируем значения
    init( rhs._intlen, rhs._fraclen );
    memcpy( _int, rhs._int, );
    memcpy( _frac, rhs._frac, );    
}


Выделять ли общую часть в отдельный метод?
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
20.04.2017, 04:56
Ответы с готовыми решениями:

Конструктор копирования и оператор присваивания
Не понимаю, когда используется один, а когда другой. Написал простой пример с комплексными числами - при компиляции в VS2010 и CodeBlock 10...

Конструктор копирования и оператор присваивания
Есть класс (синтетический, создан для примера) class Object { private: int a; float b; public: Object(): a(0),...

Оператор присваивания через конструктор копирования
Возник такой вопрос. Как перегрузить оператор присваивания для класса через конструктор копирования. Когда попытался сам, то сделал...

14
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
20.04.2017, 04:59
Цитата Сообщение от GoldenId Посмотреть сообщение
Выделять ли общую часть в отдельный метод?
ага

Добавлено через 51 секунду
Цитата Сообщение от GoldenId Посмотреть сообщение
delete[] _int;
низкоуровневые операции в юзерском коде - признак говнокода.
знак подчеркивания в начале имени - признак UB.
0
Велосипедист...
 Аватар для Mournful Max
353 / 220 / 73
Регистрация: 15.12.2015
Сообщений: 785
20.04.2017, 05:45
hoggy,
Цитата Сообщение от hoggy Посмотреть сообщение
знак подчеркивания в начале имени - признак UB.
Сейчас не совсем понял. Это стандарт языка говорит или в смысле?
0
Любитель чаепитий
 Аватар для GbaLog-
3745 / 1801 / 566
Регистрация: 24.08.2014
Сообщений: 6,020
Записей в блоге: 1
20.04.2017, 05:48
Цитата Сообщение от Captain Maxee Посмотреть сообщение
Это стандарт языка говорит
стандарт говорит, что такие имена зарезервированы для разработчиков компиляторов.
но про UB там ни слова.
hoggy, объясните, пожалуйста, почему это UB?
2
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12935 / 6802 / 1821
Регистрация: 18.10.2014
Сообщений: 17,214
20.04.2017, 05:54
Цитата Сообщение от GoldenId Посмотреть сообщение
Как лучше?
Вместо того, чтобы плодить методы, лучше воспользоваться copy-and-swap идиомой, в рамках которой конструктор копирования как раз и будет выступать в качестве того самого "отдельного метода", в котором находится "общая часть"

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Fraction::Fraction( const Fraction& rhs )
{
    init( rhs._intlen, rhs._fraclen );
    memcpy( _int, rhs._int, );
    memcpy( _frac, rhs._frac, );    
}
 
Fraction Fraction::operator = ( Fraction rhs )
{
    std::swap(_int, rhs._int);
    std::swap(_frac, rhs._frac);
    // std::swap все остальное
    return *this;
}
В такой ситуации и ваша функция init может оказаться лишней. А вот последовательность swap можно, при желании, выделить в отдельный метод или, даже лучше, friend-функцию.

Почему ваш оператор присваивания возвращает по значению - не знаю...
1
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
20.04.2017, 05:58
Цитата Сообщение от Captain Maxee Посмотреть сообщение
Это стандарт языка говорит или в смысле?
Цитата Сообщение от GbaLog- Посмотреть сообщение
объясните, пожалуйста, почему это UB?
Функция принимает любое количество строк а затем складывает их
1
 Аватар для GoldenId
142 / 143 / 64
Регистрация: 11.11.2010
Сообщений: 877
Записей в блоге: 10
20.04.2017, 06:22  [ТС]
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Почему ваш оператор присваивания возвращает по значению - не знаю...
Спасибо, не доглядел.

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
C++
1
2
3
4
5
6
7
Fraction Fraction::operator = ( Fraction rhs )
{
    std::swap(_int, rhs._int);
    std::swap(_frac, rhs._frac);
    // std::swap все остальное
    return *this;
}
То есть при передаче по значению rhs конструируется конструктором копирования, потом с ним меняются все поля: старое↔новое... Читаю.. Пока не понимаю, зачем именно swap. Почему не просто почленное присваивание...
0
Любитель чаепитий
 Аватар для GbaLog-
3745 / 1801 / 566
Регистрация: 24.08.2014
Сообщений: 6,020
Записей в блоге: 1
20.04.2017, 07:38
hoggy, это я видел, но там ни слова об UB.
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
20.04.2017, 07:53
Цитата Сообщение от GbaLog- Посмотреть сообщение
это я видел, но там ни слова об UB.
смотрите в стандарте определение термина UB.

и когда стандарт говорит:
вы не должны использовать идентификаторы:
In addition, some identifiers are reserved for use by C++ implementations and shall not be used otherwise;
а вы все равно используете,
то такой ваш код уже попадает
под определение UB.
2
20.04.2017, 07:56

Не по теме:

hoggy, а вот интересно, может быть такое, что из-за этого будет ошибка времени выполнения, а не компиляции?
можете привести пример?

0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
20.04.2017, 08:07
Цитата Сообщение от GbaLog- Посмотреть сообщение
можете привести пример?
ну вот навскидку:

http://rextester.com/ZELN59951
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <string>
 
 
std::string _value="я есть ощипко премени выполнения, оля ля ля!";
 
template<class T>
class trololo
{
public:
    trololo():
        _value("я есть приватный переменный, оло ло ло!")
    {}
    std::string _value;
};
 
template<class T>
class ololo: public trololo<T>
{
public:
    void set_value(const std::string& value)
    {
        _value = value; // <--- upps
    }
};
 
int main() 
{
    ololo<int> example_runtime_bug;
    
    std::cout << example_runtime_bug._value << std::endl;
    
    example_runtime_bug.set_value("устанавливаю значение данных-членов");
    std::cout << example_runtime_bug._value << std::endl;
    std::cout << "ой... а почему новое значение не установилось? печалько...\n";
}
это пример того,
как глобальный идентификатор запросто перекрыл мембер.
соответственно,
в рантайме получили не то,
что ожидали.

а вообще - ну фик его знает,
какая может выстрелить ситуация.
3
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12935 / 6802 / 1821
Регистрация: 18.10.2014
Сообщений: 17,214
20.04.2017, 08:50
Лучший ответ Сообщение было отмечено GoldenId как решение

Решение

Цитата Сообщение от GoldenId Посмотреть сообщение
То есть при передаче по значению rhs конструируется конструктором копирования,
Да, верно.

Цитата Сообщение от GoldenId Посмотреть сообщение
потом с ним меняются все поля: старое↔новое... Читаю.. Пока не понимаю, зачем именно swap. Почему не просто почленное присваивание...
Почленное присваивание тут никак не подойдет. В этот момент поля *this содержат старые значения, которые надо не забыть освободить. А именно - массивы _int и _frac. А rhs содержит новые значения. Если мы просто сделаем "почленное присваивание", то старые значения превратятся в утечки памяти.

Поэтому мы именно обмениваем старые с новыми через swap. Т.е. старые значения попадают в rhs. В конце работы оператора rhs будет автоматически уничтожен, а вместе с ним уничтожатся и старые значения. Разумеется, эти рассуждения относятся только к полям-указателям на дополнительные ресурсы, т.е. _int и _frac. Остальные поля можно просто присваивать. Но чисто ради единообразия обычно делают swap для всех полей.

Таким образом в рамках идиомы copy-and-swap как конструктор копирования, так и деструктор Fraction работают как методы, в которые вынесен повторяющийся код: код выделения-копирования новых значений и код освобождения старых значений соответственно. (Вы почему-то не обратили внимания на то, что у вас повторяется не только и код копирования, но код освобождения.) Идиома copy-and-swap устраняет повторения и того и другого без введения новых функций.

P.S. Отдельной прелестью такого подхода является то, что последовательность swap является операцией, безопасной с точки зрения исключений - она не выбрасывает исключений вообще. Таким образом, если где-то в процессе копирования произошло исключение, то гарантируется, что независимо от того, где оно произошло, объект-получатель либо станет корректной копией оригинала, либо вообще останется нетронутым. Невозможна ситуация, когда объект-получатель получит лишь частичную копию оригинала.
1
 Аватар для GoldenId
142 / 143 / 64
Регистрация: 11.11.2010
Сообщений: 877
Записей в блоге: 10
20.04.2017, 11:21  [ТС]
Создание дополнительного экземпляра Fraction? Что с оверхедом в строке // повторное использование кода
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bool Fraction::operator == ( const Fraction& rhs ) const
{
    // пройдём по дробной части и сравним имеющиеся цифры
    for( size_type f = std::max( fraclen, rhs.fraclen ) - 1; f != npos; f-- )
        if( ( f < fraclen ) ? frac[f] : 0 != ( f < rhs.fraclen ) ? rhs.frac[f] : 0 )
            return false;
    // то же для целой части
    for( size_type i = std::max( intlen, rhs.intlen ) - 1; i != npos; i== )
        if( ( i < intlen ) ? int_[i] : 0 != ( i < rhs.intlen ) ? rhs.intlen : 0 )
        return false;
    //throw runtime_error( "Unimplemented" );
}
 
bool Fraction::operator != ( const Fraction& rhs ) const
{
    return ! *this == rhs;      // повторное использование кода
//  throw runtime_error( "Unimplemented" );
}


Цитата Сообщение от TheCalligrapher Посмотреть сообщение
В этот момент поля *this содержат старые значения, которые надо не забыть освободить. А именно - массивы _int и _frac. А rhs содержит новые значения.
Вон оно что...

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Поэтому мы именно обмениваем старые с новыми через swap.
То есть swap не просто получает нам новые значения в lvalue-объект, но и отправляет старые на утилизацию...
Кликните здесь для просмотра всего текста
Название: 14085560668527.jpg
Просмотров: 39

Размер: 32.8 Кб


Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Вы почему-то не обратили внимания на то, что у вас повторяется не только и код копирования, но код освобождения.
Давно не брал в руки шашек ^^

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
P.S. Отдельной прелестью такого подхода является то, что последовательность swap является операцией, безопасной с точки зрения исключений - она не выбрасывает исключений вообще.
Какие страшные крокодилы:
C++
1
2
3
4
template <class T> void swap (T& a, T& b)
  noexcept (is_nothrow_move_constructible<T>::value && is_nothrow_move_assignable<T>::value);
template <class T, size_t N> void swap(T (&a)[N], T (&b)[N])
  noexcept (noexcept(swap(*a,*b)));
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Невозможна ситуация, когда объект-получатель получит лишь частичную копию оригинала.
То есть все проблемы, которые могут возникнуть в большой тройке, это повторение кода и невалидные состояния объектов, из-за частичного их построения в результате возникновения исключений, и решаются обе идиомой copy-and-swap?
0
 Аватар для GoldenId
142 / 143 / 64
Регистрация: 11.11.2010
Сообщений: 877
Записей в блоге: 10
20.04.2017, 20:21  [ТС]
Цитата Сообщение от GoldenId Посмотреть сообщение
Создание дополнительного экземпляра Fraction? Что с оверхедом в строке // повторное использование кода
Оу, нет. Не так.
Вот так
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
LongLong LongLong::operator * ( const LongLong& right ) const
{
    LongLong res( c_zero );
    low_type lm = 1;
    LongLong term = right;
    for( int d = 0; d < low_digits; d++, lm <<= 1, term <<= 1 )
        if( _low & lm )
            res += term;
 
    high_type hm = 1;
    for( int d = low_digits; d < all_digits; d++, hm <<= 1, term <<= 1 )
        if( _high & hm )
            res += term;
 
    return res;
}
 
LongLong LongLong::operator *= ( const LongLong& right )
{
    return *this = *this * right;
}


Добавлено через 7 часов 46 минут
TheCalligrapher, спасает ли copy-and-swap от самоприсваивания? ... Вроде да.

stackoverflow говорит:
A swap function is a non-throwing function that swaps two objects of a class, member for member. We might be tempted to use std::swap instead of providing our own, but this would be impossible; std::swap uses the copy-constructor and copy-assignment operator within its implementation, and we'd ultimately be trying to define the assignment operator in terms of itself!
То есть вообще говорят std::swap использовать нельзя, если члены-данные сами страдают от нереализованной copy-and-swap идиомы, но в моё случае простых типов и оригинальных указателей можно?
0
 Аватар для GoldenId
142 / 143 / 64
Регистрация: 11.11.2010
Сообщений: 877
Записей в блоге: 10
23.04.2017, 06:22  [ТС]
hoggy, GbaLog-, TheCalligrapher. Назрел ещё один вопрос по проектированию. Прошу помочь увязать создание объектов этих длинных чисел, изменение их разрядности, инициализацию значений, сдвиг и рекомендованную здесь идиому.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
23.04.2017, 06:22
Помогаю со студенческими работами здесь

Про конструктор копирования, оператор присваивания
Объясните, пожалуйста, принцип действия конструктора копирования и операции присваивания. На что указывает указатель this в этих функциях?...

Очередь, конструктор копирования и перегруженный оператор присваивания
#include &lt;iostream&gt; using namespace std; typedef char type; struct Node { type element; Node *pNext; };

Ребят, уже запарился, гляньте, что не так!? конструктор копирования и оператор присваивания
#include &lt;iostream&gt; using namespace std; struct SNode { SNode*next; int val; SNode(){} SNode(SNode*...

Нужно ли реализовать также отдельно конструктор копирования, если имеется перегруженный оператор присваивания?
у меня есть класс. и прототип перегруженной операции присваивания some_class&amp; some_class::operator=(const some_class&amp;...

Конструктор копирования, присваивания
Пусть есть класс class some{ private : int a ; }; Перегрузить оператор &quot;=&quot; можно так some&amp; operator=(const...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
SDL3 для Android: Загрузка PNG с альфа-каналом с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru