Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.50/6: Рейтинг темы: голосов - 6, средняя оценка - 4.50
Pure Free Digital Ghost
4236 / 1694 / 326
Регистрация: 06.01.2013
Сообщений: 4,311
1

Использование placement-new в перегруженном операторе присваивания

25.05.2015, 00:01. Показов 1113. Ответов 9

Всем хей.

Допустим, у меня определён конструктор копирования для класса T. Теперь я хочу перегрузить для T оператор присваивания и, чтобы не повторять код (DRY всё же), делаю так:

C++
1
2
3
4
5
T& operator = (const T& ref) {
  if (this != &ref)
     new(this) T(ref);
  return *this;
}
В целом - схема работает. Но мне хотелось бы узнать подводные камни.
Беглый осмотр в данном виде их не нашёл, утечек вроде не возникает, всё тишь да гладь...

Но не покидает ощущение, что так можно неплохо настрелять себе в ногу.
Подопытный кролик
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
 
class T {
    int *arr;
    size_t sz;
public:
    T( int st, int _sz) : sz(_sz), arr(new int[_sz]) {
        for (int i = 0; i < sz; ++i) arr[i] = i + st;
    }
    
    T(const T& ref) : sz(ref.sz), arr(new int[ref.sz]) {
        for (int i = 0; i < sz; ++i) arr[i] = ref.arr[i];
    }
    
    T( ) : sz(0), arr(nullptr) { }
    
    T& operator = (const T& ref) {
        if (this != &ref)       
            new(this) T(ref);
        return *this;
    }
    
    void print( ) {
        for (size_t i = 0; i < sz; ++i) std::cout << arr[i] << ' ';
        std::cout << std::endl;
    }
    
    ~T( ) {
        for (size_t i = 0; i < sz; ++i) arr[i] = 0;
        delete[] arr;
    }
};
 
int main ( ) {
    while (1) {
        T a(20, 30);
        T b(a);
        T c;
        c = b;
        T d;
        {
            T f(15, 10);
            d = f;
        }
        
        a.print();
        b.print();
        c.print();
        d.print();
    }
    return 0;
}


Естественно, реь идёт о тех случаях, когда от конструктора копирования и оператора присваивания требуется единообразное поведение.
1
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
25.05.2015, 00:01
Ответы с готовыми решениями:

Сложение массивов в перегруженном операторе
Здравствуйте. Я создала в мейне 2 массива и попыталась их сложить таким образом: m3 = m1 + m2;...

Об операторе присваивания
можно &quot;перевести&quot;?

Ошибка в операторе присваивания
Здравствуйте! Подскажите, пожалуйста, почему в строке: a = 7 + a + 8 + c;, возникает ошибка?...

Логическое выражение в операторе присваивания
int a=num&lt;0?-num:num; int b=den&lt;0?-den:den; Не могу понять как в данном случае работает...

9
15039 / 8058 / 1940
Регистрация: 30.01.2014
Сообщений: 13,642
25.05.2015, 00:11 2
Цитата Сообщение от FraidZZ Посмотреть сообщение
Но не покидает ощущение, что так можно неплохо настрелять себе в ногу.
Конечно можно. Не надо так делать
Вот пример:
C++
1
2
3
T a(20, 30);
T b(30, 40);
a = b; // утечка памяти из a.
2
Pure Free Digital Ghost
4236 / 1694 / 326
Регистрация: 06.01.2013
Сообщений: 4,311
25.05.2015, 00:14  [ТС] 3
Так. Вот мне и указали первую ошибку. Я забыл освободить память при переприсваивании объекта.
Правильнее всё же так, да.
C++
1
2
3
4
5
6
7
T& operator = (const T& ref) {
    if (this != &ref){
        this->~T();
        new(this) T(ref);
    }
    return *this;
}
0
15039 / 8058 / 1940
Регистрация: 30.01.2014
Сообщений: 13,642
25.05.2015, 00:16 4
FraidZZ, Для реализации твоей задумки есть куда более простое и красивое решение. Называется copy-and-swap idiom.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class T {
    int *arr;
    size_t sz;
public:
    //.......
 
    T& operator = (const T& ref) {
        if (this != &ref)
        {
            T(ref).swap(*this);
        }
        return *this;
    }
 
    //......
 
private:
    void swap(T & t)
    {
        std::swap(arr, t.arr);
        std::swap(sz, t.sz);
    }
};
И данные старые удалим и конструктор копирования в деле.
3
Pure Free Digital Ghost
4236 / 1694 / 326
Регистрация: 06.01.2013
Сообщений: 4,311
25.05.2015, 00:18  [ТС] 5
DrOffset, спасибо, но я с ней знаком. Мне был интересен именно сам вызов placement-new по адресу this
0
15039 / 8058 / 1940
Регистрация: 30.01.2014
Сообщений: 13,642
25.05.2015, 00:22 6
Цитата Сообщение от FraidZZ Посмотреть сообщение
Правильнее всё же так, да.
Проблема номер два. Получим повторный вызов деструктора (при раскрутке стека), если конструктор T сгенерирует исключение. Например std::bad_alloc здесь вполне реален.
Вывод: этот код не просто плох, а крайне плох и неверен.
2
Pure Free Digital Ghost
4236 / 1694 / 326
Регистрация: 06.01.2013
Сообщений: 4,311
25.05.2015, 00:26  [ТС] 7
DrOffset, или я в танке, или он не едет, но всё же.
В данном случае проблема вызова двух деструкторов не критична, учитывая, что каждый, по сути, вызывается для отдельного экземпляра объекта (хоть и расположены они по одному адресу)

Другой вопрос, что после выдачи исключения объект станет битым, так как в нём будет мусор, в отличие от copy-swap. Или это и имелось в виду в
Цитата Сообщение от DrOffset Посмотреть сообщение
повторный вызов деструктора
?
0
15039 / 8058 / 1940
Регистрация: 30.01.2014
Сообщений: 13,642
25.05.2015, 00:31 8
Цитата Сообщение от FraidZZ Посмотреть сообщение
В данном случае проблема вызова двух деструкторов не критична, учитывая, что каждый, по сути, вызывается для отдельного экземпляра объекта (хоть и расположены они по одному адресу)
Еще как критична.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
T a(10, 20);
T b(10, 30);
 
a = b; // здесь исключение
 
/*
T& operator = (const T& ref) {
    if (this != &ref){
        this->~T(); // это отработало, память освобождена
        new(this) T(ref); // throw ....
    }
    return *this;
}
*/
// Далее, при выходе из области видимости, где объявлены объекты a и b, их деструкторы будут вызваны, 
// получим повторный вызов delete для одного и того же куска памяти в объекте a, как следствие порчу кучи и креш.
// UB же.
1
Pure Free Digital Ghost
4236 / 1694 / 326
Регистрация: 06.01.2013
Сообщений: 4,311
25.05.2015, 00:36  [ТС] 9
DrOffset, ясно, спасибо. Всё сошлось. Деструктор вызывается уже уничтоженному объекту.
0
15039 / 8058 / 1940
Регистрация: 30.01.2014
Сообщений: 13,642
25.05.2015, 00:37 10
Цитата Сообщение от FraidZZ Посмотреть сообщение
Или это и имелось в виду в
Имелось в виду, что мы ходим по краю UB
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
25.05.2015, 00:37

Помощь в написании контрольных, курсовых и дипломных работ здесь.

Ссылка rvalue в конструкторе копирования и операторе присваивания
Здравствуйте. Решил попробовать ссылки rvalue в классе. Привожу часть: \\BinaryArray.h ...

Использование вектора для присваивания значений элементам структуры
Здравствуйте, форумчане. Сразу отмечу, что я новичек, так что, возможно, мой вопрос покажется вам...

WPF + Excel данные - IXLWorksheet использование в операторе using
Доброе время суток коллеги, подскажите как решить проблему : IXLWorksheet: тип, использованный в...

Placement new делигирование конструкторов
Вычитал, что до С++11 и делигирования конструкторов использовали такой ход конём, но я слышал о...


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

Или воспользуйтесь поиском по форуму:
10
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.