#cpp
Что представляет собою макрос __STL_DEFAULT_ALLOCATOR(T), а так же макрос __STL_NOTHROW. И - самое главное - как ими пользоваться? Я - в качестве эксперимента - пытаюсь написать "свой собственный" "вектор" и выделить для него память при помощи аллокатора. А точнее просто пытаюсь понять, как пользоваться готовым библиотечным аллокатором? МОЙ ВОПРОС : В векторе не работают конструкторы. Не знаю почему. Привожу код. template> class Vec{ T* bbb ; T* eee ; T* mmm ; const A* a ; public : Vec(const A & aaa = A()) :bbb(nullptr),eee(nullptr),mmm(nullptr) ,a(&aaa) // Здесь всё нормально. {} Vec( size_t n , const A & aaa = A() ) :a(&aaa) { bbb = a->allocate(n) ; // Тут что-то не так. // Без этой строки - ошибка вермени выполнения. uninitialized_fill_n(bbb,n,T()) ; eee = bbb ; mmm = bbb + n ; } Vec( int n , T t , const A & aaa = A()) :a(&aaa) { uninitialized_fill_n(bbb,n,t) ; eee = bbb + n ; mmm = bbb + n ; // Не работает. } Vec(const Vec< T, A> & vc) { mmm = uninitialized_copy(vc.bbb,vc.mmm,bbb) ; eee = uninitialized_copy(vc.bbb,vc.eee,eee) ; // Не проверял. } Я работаю в Visual C++ 2010 Express. Мне не понятно, - как аллокатор выделяет память внутри сигнатуры конструктора. ОБНОВЛЕНИЕ : Здравствуйте уважаемый ixSci! Прошу прощение за слишком краткое описание проблемы. Мне казалось, что и так будет всё понятно. ИТАК. Без строки a->allocate(n) возникает ошибка времени выполнения. При наличии данной строки Компилятор пишет следующее: error C2663: 'std::allocator<_Ty>::allocate' : 2 overloads have no legal conversion for 'this' pointer Это сообщение происходит ТОЛЬКО при использовании данного конструктора. То есть, когда в исполняемом коде возникает объект, использующий этот конструктор. Например: Vec vc(5). Теперь о моей компетенции. Я прочитал Г.Шилдт"Базовый курс С++" и заканчиваю изучать учебник С.Прата"Язык программирования С++ Лекции и упражнения". Опыт, конечно, небольшой. Но по меткому выражению Мэтью Уилсона, - "очень люблю заглядывать под капот." Кстати, в первом коде, который Вы мне прислали есть ошибка: Функция construct() имеет второй аргумент - ссылку на значение инициализирующего объекта. Без этого аргумента функция не работает. Есть и другие ошибки. Но в целом код даёт понимание, - как работает Аллокатор. Так что всё равно - СПАСИБО! Я добавил в Ваш код некоторые свои комментарии. Если хотите, я покажу то, что у меня получилось. И ЕЩЁ. Если можно, покажите, пожалуйста как должен выглядеть "НОРМАЛЬНЫЙ" вектор в котором память выделяется при помощи Аллокатора. Только "шапку", поля данных и ОДИН конструктор, создающий объект из "n" элементов и их инициализирующий. СПАСИБО! ОБНОВЛЕНИЕ 2 Уважаемые господа @ixSci и @Abyx ! Огромное СПАСИБО! Благодаря Вам я познакомился с Аллокаторами. Теперь я пришёл к ряду выводов. Хочется узнать, - верны ли они??? ВЫВОДЫ: Аллокатор как параметр присутствует во всех конструкторах библиотечного контейнера vector только для инициализации его, - вектора, - базового класса, в котором и хранится Аллокатор. Мои конструкторы вектора не работали именно потому, что в них Аллокатор, уже объявленный в шаблоне класса, - повторно объявлялся и инициализировался. Если удалить из памяти объекты при помощи методаdeallocate(), предварительно не разрушив их при помощи методаdestroy(), то эти объекты могут так и остаться в куче, что вызовет утечку памяти. Закрытые поля данных m_Start,m_Finish,m_Storage следует помещать в конце объявления класса. Очевидно для того, чтобы Аллокатор быстрее построил Вектор и отключился. У меня остались вопросы. Зачем оставлять объекты в куче, в то время, как доступа к этим объектам уже не существует? То есть, почему бы не объединить destroy() и deallocate() в один метод? Зачем в действительности поля m_Start,m_Finish,m_Storahe помещаются в конец объявления Вектора? Притом, что если эти поля поместить в начало Объявления Вектора, всё и так будет работать?
Ответы
Ответ 1
__STL_DEFAULT_ALLOCATOR(T) и __STL_NOTHROW это какие-то макросы, которые та или иная реализация библиотеки C++ использует. Они совершенно не нужны, для использования аллокатора. Вот Вам пример использования стандартного, библиотечного аллокатора с комментариями: #includestruct A { int a = 0; int b = 1; int c = 2; }; int main() { std::allocator allocator; size_t howMuch = 50; // Выделяем память под 50 структур A. Возвращённый указатель, указывает на неинициализированную память. A* allocatedArray = allocator.allocate(howMuch); A* allocatedItem = allocator.allocate(1); // Т.к. память не инициализирована, нам нужно её инициализировать. Для этого вызываем конструктор A явно allocator.construct(allocatedItem); // Для инициализации памяти под массив есть вспомогательная функция, которая сделает то же самое, что // делает allocator по умолчанию std::uninitialized_fill_n(allocatedArray, howMuch, A()); // После того, как наигрались с объектом, его нужно удалить, но сначала нужно вызвать деструктор allocator.destroy(allocatedItem); for(size_t i = 0; i < howMuch; ++i) allocator.destroy(allocatedArray + i); // Теперь освобождаем выделенную память allocator.deallocate(allocatedItem, 1); allocator.deallocate(allocatedArray, howMuch); return 0; } ОБНОВЛЕНИЕ: Во-первых, не храните указатель на аллокатор в объекте, храните сам объект аллокатора. Более того, мне не ясно, зачем Вы его копируете в конструкторе. Просто создавайте объект аллокатора согласно параметру шаблона и всё. Во-вторых, у Вас есть очевидные ошибки в 2-х конструкторах: Vec(int n, T t, const A & aaa = A()) :a(&aaa) { uninitialized_fill_n(bbb, n, t); eee = bbb + n; mmm = bbb + n; // Не работает. } Конечно не работает, кто будет память под bbb выделять? Добавьте: bbb = a->allocate(n); Vec(const Vec< T, A> & vc) { mmm = uninitialized_copy(vc.bbb, vc.mmm, bbb); eee = uninitialized_copy(vc.bbb, vc.eee, eee); // Не проверял. } Тот же комментарий про память, а потом нужно одно копирование: Vec(const Vec< T, A> & vc) { bbb = a->allocate(vc.mmm - vc.eee); mmm = uninitialized_copy(vc.eee, vc.mmm, bbb); eee = bbb; } Ну а по поводу: // Тут что-то не так. // Без этой строки - ошибка вермени выполнения. Я не понимаю, что Вы этим хотели сказать. Что не так? Без какой строчки? В целом, у меня создалось впечатление, что в C++ Вы очень сильно плавает, а лезть в аллокаторы, плавая в C++, это гиблое дело. ОБНОВЛЕНИЕ 2: Первое, код приведённый мной абсолютно корректен. Используйте последние доступные компиляторы, а не MSVS2010, который устарел. Это же касается нижеприведённого кода - используйте MSVS2013 для его компиляции. Вот Вам минимальный пример, который содержит конструктор и деструктор для своего вектора: template > class Vector {; public: Vector() { //Выделим немного памяти, чтобы не делать это при первом же push_back m_Storage = m_Allocator.allocate(m_Capacity); } Vector(size_t size): m_Size{size}, m_Capacity{size*3/2} { //Выделим памяти немного больше, чем просили. m_Storage = m_Allocator.allocate(m_Capacity); //Инициализируем память, т.к. конструктор подразумевает созданные объекты std::uninitialized_fill_n(m_Storage, m_Size, T{}); } ~Vector() { for(auto it = m_Storage; it != m_Storage + m_Size; ++it) m_Allocator.destroy(it); m_Size = 0; m_Allocator.deallocate(m_Storage, m_Capacity); } size_t size() const { return m_Size; } private: T* m_Storage; Alloc m_Allocator; size_t m_Size = 0; size_t m_Capacity = 10; }; ОБНОВЛЕНИЕ 3 Касательно Ваших выводов: Вывод №2 неверный, deallocate освободит ранее выделенную память, но без destroy не будут вызваны деструкторы объектов, который были помещены в вектор. Вызовет ли это утечку? Это зависит от объектов, которые хранит вектор. Если это Vector , то никаких проблем не будет. Если это Vector , то память, которую string выделяет внутри себя никогда не будет освобождена. destroy обязательно должен быть вызван. Вывод №3 безосновательный. Члены класса можно помещать хоть в начало, хоть в конец. Это дело вкуса и ни на что не влияет. Просто я помещаю их в конец, я так привык. По поводу вопроса №1: Потому что у аллокатора может быть такое внутреннее устройство, что он никогда не освобождает память, а лишь конструирует/разрушает объекты. Кроме того, операции выделения памяти и конструирования это две совершенно разные операции, которые должны иметь свои контр-пары. Их объединение не имеет смысла. P.S. @NZMEY, пора прекратить эксплуатировать этот вопрос. Мы уже превращаем его в форум, а движок SO для этого не предназначен. Если будут ещё вопросы - создавайте ещё темы.
Комментариев нет:
Отправить комментарий