Подтвердить что ты не робот

Двойное удаление в файле initializer_list vs 2013

Сегодня при запуске проблемы с памятью в моем проекте с классом с использованием С++ 11 initializer_list. Система сигнализирует о проблеме с памятью: "Выражение _BLOCK_TYPE_IS_VALID (pHead- > nBlockUse) в dbgdel.cpp. Я упростил код до простого примера, он больше не выдает выражение, но проблема становится очевидной из вывода отладки. код правильный, также он работает с g++.

#include <functional>
#include <memory>
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <sstream>

#include <initializer_list>

using namespace std;

class B {
public:
    char data[256];
    B(const string& x) {
        cout << "Init " << this << endl;
    }
    B(const B& b) {
        cout << "Copy " << this << endl;
    }
    ~B() {
        cout << "Deleting b " << this << endl;
    }
};

class C {
public:
    vector<B> bs;

    C(initializer_list<B> bb) {
        for(auto& b : bb) {
            bs.push_back(b);
        }
    }
};

int main(int argc, char** argv) {
    C bb { B("foo"), B("bar") };
    return 0;
}

Вывод:

Init 00B7FAE8
Init 00B7FBE8
Copy 00E108A0
Copy 00E10AE8 (?????)
Deleting b 00E108A0
Copy 00E10BE8
Deleting b 00B7FBE8
Deleting b 00B7FAE8
Deleting b 00B7FAE8  (Deleted twice!)

Какую ошибку я делаю здесь или это не должно работать?

4b9b3361

Ответ 1

Поведение initializer_list ошибочно. В своем деструкторе он вызывает векторное удаление (a delete []) всего диапазона, а затем снова удаляет первую запись в массиве. Это поведение не является частью класса initializer_list и выглядит как ошибка компилятора. initializer_list не имеет деструктора и не выделяет массив, используемый для списка. Он просто выглядит как оболочка для массива C.

Что касается использования дополнительной копии, которую вы видите, это вызвано изменением размера вектора во время его инициализации. Вот ваш поток:

Init 00B7FAE8       // construct "foo"
Init 00B7FBE8       // construct "bar"
Copy 00E108A0       // copy "foo" to vector (capacity=1)
Copy 00E10AE8 (?????) // copy the above object to the resized vector (capacity = 2)
Deleting b 00E108A0   // delete the smaller vector buffer
Copy 00E10BE8         // copy "bar" from initialization_list to vector

Deleting b 00B7FBE8   // delete initialization_list in reverse order. this is "bar"
Deleting b 00B7FAE8   // last to delete. this is "foo"

Deleting b 00B7FAE8  (bug)

// later C::bs is destroyed

Что вы видите здесь, это инициализация вектора через push_back довольно медленно из-за копирования. Это произойдет, даже если вы использовали более элегантный способ:

C(initializer_list<B> bb) : bs(bb) {}

Более быстрый (без дополнительных копий) метод:

C(initializer_list<B> bb) {
    bs.reserve(bb.size());
    bs.insert(bs.end(), bb.begin(), bb.end());
}