Форум программистов, компьютерный форум CyberForum.ru

C++

Войти
Регистрация
Восстановить пароль
 
FraidZZ
Ex-Member
3897 / 1523 / 229
Регистрация: 06.01.2013
Сообщений: 4,050
Завершенные тесты: 1
#1

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

25.05.2015, 00:01. Просмотров 422. Ответов 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;
}


Естественно, реь идёт о тех случаях, когда от конструктора копирования и оператора присваивания требуется единообразное поведение.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
25.05.2015, 00:01
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Использование placement-new в перегруженном операторе присваивания (C++):

Union, new placement, strict-aliasing, cross-platform - C++
Доброго времени суток. Ниже представленный код вроде бы работает. Гонял его на компиляторах cl/mingw ...

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

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

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

New placement - C++
Что это за юмор такой? #include &lt;iostream&gt; #include &lt;new&gt; using namespace std; int main() { double *a= new...

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

Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
DrOffset
7092 / 4233 / 950
Регистрация: 30.01.2014
Сообщений: 7,008
25.05.2015, 00:11 #2
Цитата Сообщение от FraidZZ Посмотреть сообщение
Но не покидает ощущение, что так можно неплохо настрелять себе в ногу.
Конечно можно. Не надо так делать
Вот пример:
C++
1
2
3
T a(20, 30);
T b(30, 40);
a = b; // утечка памяти из a.
FraidZZ
Ex-Member
3897 / 1523 / 229
Регистрация: 06.01.2013
Сообщений: 4,050
Завершенные тесты: 1
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;
}
DrOffset
7092 / 4233 / 950
Регистрация: 30.01.2014
Сообщений: 7,008
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);
    }
};
И данные старые удалим и конструктор копирования в деле.
FraidZZ
Ex-Member
3897 / 1523 / 229
Регистрация: 06.01.2013
Сообщений: 4,050
Завершенные тесты: 1
25.05.2015, 00:18  [ТС] #5
DrOffset, спасибо, но я с ней знаком. Мне был интересен именно сам вызов placement-new по адресу this
DrOffset
7092 / 4233 / 950
Регистрация: 30.01.2014
Сообщений: 7,008
25.05.2015, 00:22 #6
Цитата Сообщение от FraidZZ Посмотреть сообщение
Правильнее всё же так, да.
Проблема номер два. Получим повторный вызов деструктора (при раскрутке стека), если конструктор T сгенерирует исключение. Например std::bad_alloc здесь вполне реален.
Вывод: этот код не просто плох, а крайне плох и неверен.
FraidZZ
Ex-Member
3897 / 1523 / 229
Регистрация: 06.01.2013
Сообщений: 4,050
Завершенные тесты: 1
25.05.2015, 00:26  [ТС] #7
DrOffset, или я в танке, или он не едет, но всё же.
В данном случае проблема вызова двух деструкторов не критична, учитывая, что каждый, по сути, вызывается для отдельного экземпляра объекта (хоть и расположены они по одному адресу)

Другой вопрос, что после выдачи исключения объект станет битым, так как в нём будет мусор, в отличие от copy-swap. Или это и имелось в виду в
Цитата Сообщение от DrOffset Посмотреть сообщение
повторный вызов деструктора
?
DrOffset
7092 / 4233 / 950
Регистрация: 30.01.2014
Сообщений: 7,008
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 же.
FraidZZ
Ex-Member
3897 / 1523 / 229
Регистрация: 06.01.2013
Сообщений: 4,050
Завершенные тесты: 1
25.05.2015, 00:36  [ТС] #9
DrOffset, ясно, спасибо. Всё сошлось. Деструктор вызывается уже уничтоженному объекту.
DrOffset
7092 / 4233 / 950
Регистрация: 30.01.2014
Сообщений: 7,008
25.05.2015, 00:37 #10
Цитата Сообщение от FraidZZ Посмотреть сообщение
Или это и имелось в виду в
Имелось в виду, что мы ходим по краю UB
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
25.05.2015, 00:37
Привет! Вот еще темы с ответами:

Popover placement bootstrap (arrow) - jQuery
Всем привет. Я решил создать данную тему, так как нуждаюсь в вашей помощи. Я сегодня целый день провел за тем, чтобы сделать...

Bootstrap tooltips расположение data-placement - HTML, CSS
Добрый день ув. пользователи! Подскажите пожалуйста, можно как-то менять tooltips расположение data-placement на больших и маленьких...

Check for the proper placement of different brackets in a given string (including nested brackets). - Turbo Pascal
Ребят помогите пожалуйста с одной задачей)) уже третий день мучаюсь)) Check for the proper placement of different brackets in a...

size_t в операторе for - C++
Здравствуйте. Возникла проблема с типом size_t. Данный цикл почему то бесконечен for ( size_t j = 10; j &gt;= 0; j-- ) { ; } Если...


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

Или воспользуйтесь поиском по форуму:
Yandex
Объявления
25.05.2015, 00:37
Ответ Создать тему
Опции темы

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