Двусвязный список шаблонных классов

 
0
 
C++
ava
BearFear | 25.09.2013, 18:30
Вопрос вот в чем. Сам не знаю зачем мне это. Хотя цель примерно следующая: сохранить указашки на объекты и удалить если надо объекты вместе с хранителями их указателей. И вместо тысячи слов, быдлокод в студию!


template <typename type> class owner {
public:
    owner(type* Object, owner* Left, owner* Right) {
        _Object = Object;
        _Left = Left;
        _Right = Right;
        
        if (_Left != NULL)
            _Left->_Right = this;
        
        if (_Right != NULL)
            _Right->_Left = this;
    }
    
    ~owner() {
        if (_Object != NULL)
            delete _Object;
    }
    
    void link_left(owner* Left) {
        _Left = Left;
    }
    
    void link_right(owner* Right) {
        _Right = Right;
    }
    
    owner* get_left() {
        return _Left;
    }
    
    owner* get_right() {
        return _Right;
    }
    
    void delete_this() {
        if (_Left != NULL)
            _Left->_Right = _Right;
        
        if (_Right != NULL)
            _Right->_Left = _Left;
        
        delete this;
    }
    
    void delete_chain() {
        ////    ....
        //    - создается вектор указателей на все следующие объекты цепи
        //    - удаляется каждый объект цепи из вектора
    }
    
private:
    type* _Object;
    
    owner* _Left;
    owner* _Right;
};


Использование


int main(int argc, char** argv) {
    // Проверка размеров параметризированных классов
    owner<char> OwnerA(NULL, NULL, NULL);
    owner<long long> OwnerB(NULL, NULL, NULL);
    printf("%u %u\n", sizeof(OwnerA), sizeof(OwnerB));
    //    размеры совпадают, следовательно, произвольному стороннему классу в данном маневре пофигу
    //    чем параметризирован удаляемый объект. Хоть int, хоть char, хоть chinese_soldiers
    
    int* Int = new int; // создаем содержимое под который подгоняем наш шаблонокласс
    char* Char = new char; // создаем содержимое под который подгоняем наш шаблонокласс
    
    owner<int>* Owner_A = new owner<int>(Int, NULL, NULL); // создаем объект параметризируя типом хранимого в шаблоноклассе
    owner<char>* Owner_B = new owner<char>(Char, reinterpret_cast<owner<int>>(Owner_A), NULL); // аналогишно
    
    delete Owner_A;
    delete Owner_B;
    
    // Итого - мы можем иметь цепь анонимных объектов
    // мы можем производить наФигацию по цепи
    // мы фактически храним указатели на разные объекты и можем в случае "ЧО" слить всю цепь без последствий.
    // Правильно ли так поступать? Или кришна не на моей стороне?
    return 0;
}


Насколько опрометчиво использовать подобный подход?
Comments (44)
ava
volatile | 25.09.2013, 18:58 #
Цитата (BearFear @  25.9.2013,  17:30 findReferencedText)
Итого - мы можем иметь цепь анонимных объектов

Кто за типами будет следить?
вы их правильно удалить даже не сможете, без каста к правильному типу, который никто не знает.
я уж не говорю о чем-то другом.
Короче, бред.
ava
BearFear | 25.09.2013, 19:38 #
А более подробно можно объяснить?
ava
akizelokro | 25.09.2013, 20:20 #
пуристы сказали бы ещё, что Int и Сhar мало того, что не очень удачные названия переменных, но их и нужно delete перед завершением программы.

Скажу проще, что классический ООП фактически завершается после применения reinterpret_cast.
потому что в этом случае проще применять в owner указатели "void *" и без всякого шаблона, а дальше этот указатель приводить к желаемому для вас типу. (Или проще, такой код никому из сторонников объектно-ориентированной парадигмы не понравится, он методологически ближе к "выкрутасам" доброго старого С)
ava
BearFear | 25.09.2013, 20:42 #
Этот код не для того что бы удовлетворить эстетические потребности читающего. Это формулировка и вы сами прекрасно знаете, на скорую руку писать мегасверхкрасивый код смысла нет, он затрется. А на счет реинтерпрета согласен. Это и смущает. Но по факту, 12 байт размер данного по приведенному указателю. Не по приведенному размер такой же. В диспатчере МС просмотрел выделяемые данные. Вроди бы как освобождается норм. За кадром использовал более весомые данные для хранения в шаблоноклассах. Это меня и смутило - неужели работает? Хотелось бы услышать более глубокие ответы, как от профессионалов. Теоретически я и сам не согласен с этим кодом по множеству пунктов и лишний раз напоминать об эстетике каких то моментов... ну господа, это не серьезно.
ava
akizelokro | 25.09.2013, 20:59 #
Цитата (BearFear @  25.9.2013,  20:42 findReferencedText)
 в деструкторе owner (за пределами main - там при прочтении всего сообщения) есть delete тех некрасивых Int и Char. Ребят, если вам не интересно, то ну не пишите и не читайте совсем тогда. Вы и себе время сэкономите и мне не придется как то реагировать на заведомо "недочитанное" вами


Прошу прощения, просто не ожидал.
ava
BearFear | 25.09.2013, 21:02 #
Высокомерие?  smile 
ava
akizelokro | 25.09.2013, 21:04 #
Цитата (BearFear @  25.9.2013,  21:02 findReferencedText)
Высокомерие?


Никакого высокомерия. Просто привык, что объекты лучше удалять на том же "уровне" кода, где они были созданы. Для наглядности.
ava
BearFear | 25.09.2013, 21:11 #
Да. Это самый минимальный из минимального. Больше никаких пристроек. Вообще, хотел сделать некое подобие очищалки, для более безопасного вызова экцепшенов. В случае экцепшена опрашивается манагер цепи и он то уже зачищает все новые объекты. В общем самый важный и важнее всех важных вопросов данного топика - есть ли подводные камни именно у этого кода. Не гепотетически, не предполагая всевозможных других использований (не люблю гонку за полтергейстами), а именно то как вот есть. Сам принцип. А небезопасности везде хватает. По дурости как говорится можно и луже утонуть. чтож теперь, бегать за лужами и знаки ставить? Или того лучше дружиников заставить дежурить у луж?  smile 
ava
baldina | 25.09.2013, 21:25 #
прочитал два раза, но не уверен что понял зачем все это. если цель в
Цитата (BearFear @  25.9.2013,  17:30 findReferencedText)
// Итого - мы можем иметь цепь анонимных объектов
  // мы можем производить наФигацию по цепи
  // мы фактически храним указатели на разные объекты и можем в случае "ЧО" слить всю цепь без последствий.

то не проще ли что-нить вроде std::list<std::shared_ptr<boost::any>>>? причем без косяков в отношении типов, о которых сказал volatile
ava
BearFear | 25.09.2013, 22:09 #
Шаред поинтер сработает в случае SEH? Интересно, где я был когда ввели такую возможность в буст?  smile 
Если бы я спросил про АНАЛОГИ, может было бы и к месту. Программистов считают людьми, которые воспринимают все слишком буквально. В данных случаях очень много "неявностей" пришлось увидеть. Неизвестные кодеры которые якобы могут вмешаться в код. Предположение о поиске аналогов... Эх, или я не так выражаюсь или собеседники непонятливые.

Уважаемые, о том ЗАЧЕМ это нужно я уж как то сам разберусь. Тема ведь не - помогите разобраться зачем мне это и как это использовать?
ava
baldina | 26.09.2013, 00:48 #
Цитата (BearFear @  25.9.2013,  22:09 findReferencedText)
Шаред поинтер сработает в случае SEH?

точно так же, как и ~owner()
зачем вам SEH я не спрашиваю, т.к. это не моё дело и вопрос не об этом  smile 

Цитата (BearFear @  25.9.2013,  22:09 findReferencedText)
Уважаемые, о том ЗАЧЕМ это нужно я уж как то сам разберусь.

никто в этом и не сомневается. но раз вопрос задан, нам тоже надо немного понять о чем идет речь. из кода вашего логическая цепь у меня как-то не выстроилась...
вопрос был "насколько опрометчиво", не уточняя, с какой позиции рассматривать. скажем, с позиции повторного использования - опрометчиво. с позиции концепции сборки мусора - опрометчиво. с позиции масштабирования - ну если не опрометчиво, то как минимум сомнительно. с позиции С++ после замечания volatile и вашей ремарки, что "удаление объекта это лишь освобождение объема данных" и говорить нечего.
ava
BearFear | 26.09.2013, 07:34 #
Вызвать ~owner из seh хандлера куда проще чем n деструкторов по неизвестным адресам. Поэтому я могу даже гарантировать, что ~owner будет вызвае.
ava
volatile | 26.09.2013, 08:39 #
Цитата (BearFear @  25.9.2013,  19:38 findReferencedText)
А более подробно можно объяснить? Ведь удаление объекта - это освобождение определенного объема данных, никакой магии. В данном случае размер отслеживается.


BearFear, просто попробуйте написать процедуру удаления вашей цепочки.
А мы посмотрим  smile 
ava
BearFear | 26.09.2013, 11:49 #
У нулевого звена (адрес которого и хранится в манагере) как и у других звеньев, есть метод delete_chain(). Он будет отвечать за удаление ОТ текущего звена до последнего элемента связи. За срок жизни программы, данная манипуляция будет проводиться 1 раз.

void owner::delete_chain() {
    if (_Right != NULL)
        _Right->delete_chain();
    delete this;
}


Соответственно, при вызове метода удаления динамического объекта, будет вызываться удаление из цепи. Это будет связывание левого и правого элемента и удаление текущего. Организация подобного в виде массива накладно. Свести в один массив кучу указателей а потом еще и пытаться туда дописать... А такая штуковина не требует индексации, поэтому тут совершенно ненужен  учет количества. Все что смущает, это верность следующего утверждения: одинаково ли удалятся шаблонные объекты, если был вызван метод класса не зависящий от параметров шаблонного класса, который в свою очередь дернул деструктор внутри себя. Если это так, то ... то круто! Более навороченного варианта и не требуется.
ava
xvr | 26.09.2013, 12:00 #
Во первых ваш код не скомпилится - использовать просто owner в нем самом нельзя, т.к. owner это шаблонный класс, и должно быть как минимум owner<какой то тип>

Исли заменить все owner на owner<type>, то все соберется, но тут начнутся проблемы в другом месте. В том, где у вас reinterpret_cast<owner<int>*>.

Проблема в том, что delete не только освобождает память (это он сделает правильно), но и вызывает деструктор. Если у вас в owner попадут не char/int, а классы со своими деструкторами, вы получите вызов не того деструктора

ava
BearFear | 26.09.2013, 12:45 #
xvr, а повлияет ли тот факт, что в шаблонном классе хранится не данное, а типизированный указатель? Он же по идее должен быть лонг указателем грубо говоря? Я имею ввиду, что шаблонный овнер содержит не данное, поэтому тип параметризации становится неважным. Или я ошибаюсь?
ava
akizelokro | 26.09.2013, 13:09 #
Вчера хотел вякнуть про удаление объектов и необходимость перезагрузки delete().
Но я не особо лихой программер, и что там функция free будет делать и как всё это прописать получше
ava
volatile | 26.09.2013, 14:27 #
Цитата (BearFear @  26.9.2013,  12:45 findReferencedText)
повлияет ли тот факт, что в шаблонном классе хранится не данное, а типизированный указатель?

Типизированный указатель, тип которого не соответствует реальному типу данных.
ava
BearFear | 26.09.2013, 15:09 #
Да, но ведь как не крути, это всего лишь лонг. Типы отслеживаются только на стадии компиляции. За пределами компилятора можно лишь частично узнать тип, скорее всего без преобразований туда-сюда (я имею ввиду преобразования прямо в лоб, не динамик-касты). Следовательно, программе должно быть пофигу на что указывается лонг. Могут быть оверхеды, об их существовании ходят мифы, но я так и не понял есть ли они. Если они есть, то дело плохо. Но чаще я встречал инфу о том, что С++ один из немногих языков не имеющий оверхедов касающихся типов. Следовательно, имеется ряд объектов:
1 - объект владеющий элементом цепи
2 - элемент цепи владеющий данным
3 - данное

При удалении элемента цепи взаимодействуют 1>2 затем 2>3. То есть, связи между 1 и 3 нет, и объект 1 и не должен знать о том что содержит 2й. Единственное что может повлиять - если деструктор в памяти принимает аргумент при вызове, который как то указывает на тип, что в свою очередь влияет на размер удаляемого данного. Но по мне это не должно быть так. Ну я бы так не стал делать. Потому что в этом случае, неправильный вызов деструктора привел бы к плачевным последствиям. Это все балабольство конечно, мысли в слух.
ava
volatile | 26.09.2013, 18:40 #
Цитата (BearFear @  26.9.2013,  15:09 findReferencedText)
Могут быть оверхеды

BearFear, у вас не могут быть оверхеды, а точно будут сегфолты.
Вы кастанули указатель, и я не вижу где вы его кастуете обратно, к реальному типу.
пишете много букв... как обчно, смысла в них нет.
ava
akizelokro | 26.09.2013, 19:14 #
У вас здесь может быть всё, что угодно.
Мы здесь говорите на разной терминологии.
Один в рамках чистого ООП, при котором код передаётся спокойно от одного к другому программисту (и пишется исходя из этого). И достаточно проблематично, кстати, написать неправильно код по высвобождению памяти для обьектов в принципе правильно оформленного двусвязного списка. Другой пишет код в стиле "выкрутасного" С в ООП парадигме и при этом запрашивает совета большого числа программистов, отрицая сам факт, что его код будут просматривать (и фактически применять другие программисты).
Наряду логический дискурс.
Что интересно. Можно было бы написать и так, если вспомнить, как же я всё-таки выделял память лет 10 назад для Сшных программ, и можно ли там правильно освобождать память, исходя только из одного указателя (или хэндлера на выделенный блок памяти, чтобы вне зависимости от размера объекта, память высвобождалась правильно). Вне зависимости от того, какой объект вы собираетесь подвести под перегруженный (должен быть перегруженный) delete().
Вообще сама задача нисколько непохода на wrappers для указателей, в ней берутся уже неизвестно где (на уровень кода выше) объекты, предварительно построенные на предположении о том, что память для них выделена динамически (ладно, для чистоты эксперимента поверим) и сам класс (обрывок кода по сути) должен правильно удалить этот объект. Либо, в предположении автора, объекты могут удаляться неправильно, но автор сделает так, что последствия будут незначительны smile 

А можно для попытки изврата и привести примеры, где автор сделает так, что последствия усугубят "undefined behaivour" настолько, что слетит система.
ava
BearFear | 26.09.2013, 23:02 #
volatile, Из ничего всего не бывает. Если для вас букв слишком много, вас никто не заставляет их читать. Впрочем вы можете и не доводить до конца формулировку вашего кода. К чему много букв кода, когда можно просто писать un in v = ; И если есть какие то детальные понимания у вас, то изложите. Из вашей немногословности, лично я мало что могу понять, все выглядит как простые выкрики из толпы: ".... бред..... фигня.... не получится....". Ну если вы владеете истиной, откройте ее для меня, я за это скажу много раз спасибо.
ava
BearFear | 26.09.2013, 23:39 #
Ну неужели только наследование? Ведь все очень рядом и близко, неужели в С++, для решения такой простой задачи надо использовать одну из возможностей языка под названием ООП? Или костыли привязанные бинтами к голове. Работа с типами завершается до запуска программы. Почему бы не создать простейшую возможность проведения типа. Ведь сторонними средствами написанными на том же С++ я вполне могу дозавершить это дело. Но делать лишний прогон сырцов через свои тулзы, затем пропускать новые образцы через препроцессор, компилятор, линковщик. А если это шаредлиб, то сначала тулза, препроцессор, ... а потом и вовсе другое приложение зависимое от либы. Ну это же пипец одним словом. Уже не говоря о красивешем мейке, который ваще давно пора заменить на что то более человекоподобное.
Короче я ушел пить. В понедельник может залезу в оллидбг, ща уже чот не хочется ковыряться в этом. Ну все это к черту!
ava
volatile | 27.09.2013, 00:27 #
Цитата (BearFear @  26.9.2013,  23:02 findReferencedText)
Ну если вы владеете истиной, откройте ее для меня, я за это скажу много раз спасибо. 


struct big { char a [1000000000]; };
big * p = (big*) new int;
delete p;


Так в С++ делать НЕЛЬЗЯ!
(у вас если отбростить кучу застилающего хлама, делается именно так)  

Вы это понимаете?
ava
BearFear | 27.09.2013, 07:57 #
Такой большой массив за один раз не получится выделить. В вашем коде, переназначается данное указателя. У меня такого не происходит. А вот то что может удалиться значение ПОСЛЕДНЕГО каста... в это я еще поверю. Я и сам знаю что такое могло произойти. В общем ладно, оставлю обсуждение до понедельника, там может (если руки дойдут) на асме поковыряюсь, может вставками на асме добью это дело. Просто наследование и удаление через суперкласс с виртуальным деструктором не очень хочется. Не все классы получится наследовать. И сам факт виртуального деструктора, там же как то поиск производится, на это уходит времени куда больше, чем при прямом обращении к не виртуальному деструктору.
ava
mes | 27.09.2013, 08:31 #
Цитата (BearFear @  25.9.2013,  20:11 findReferencedText)
. Вообще, хотел сделать некое подобие очищалки, 

BearFear,  храните полиморфный deleter (например void (*deleter)(void *)) в паре с указателем и будет вам счастье  smile
для определения делетера используется шаблон. подобное решение можно применять всегда , когда на месте применения важен не тип, но действие..  подробнее поиском по "type erasure" 
ava
BearFear | 27.09.2013, 08:32 #
mes, о, понял о чем вы. Сейчас голова проснется, попробую.
ava
mes | 27.09.2013, 08:33 #
Цитата (BearFear @  27.9.2013,  06:57 findReferencedText)
 Просто наследование и удаление через суперкласс с виртуальным деструктором не очень хочется.

это избыточнее решение.. 
ava
BearFear | 27.09.2013, 08:38 #
mes, ДА! ООП в данном случае - это очень много действий вокруг и около!
ava
mes | 27.09.2013, 08:40 #
Цитата (BearFear @  27.9.2013,  07:38 findReferencedText)
 ООП в данном случае - это очень много действий вокруг и около! 

не ооп, а использование встроенной виртуальности.. хотя и так тоже можно, но технология называется по другому... также имеется пример по приведенной выше ссылке smile
ava
BearFear | 27.09.2013, 08:50 #
Только вот сообразить не могу. Ок есть шаблонная процедура которая на входе определяет тип и удаляет его. С удалением проблем нет. Но что в нее подать и откуда? Сохранить данные в ряд можно либо через void*, либо...
Ладно, сайчас кофе подействует, до работы доберусь,  должно что то родиться.
ava
mes | 27.09.2013, 09:05 #
что то типо этого :

struct closure
{
  void const* pointer;
  void (*fn)(void const*) 
};

void call(closure const& c)
{
   c.fn(c.pointer);
}

template<typename T> 
struct deleter_fn
{
  static fn(void const* ptr)
  {
      delete reinterpret_cast<T const*>(ptr)
  }
}


template<typename T> 
closure make_deleter(T const* ptr)
{
  closure c;
  c.pointer = ptr;
  c.fn = delete_fn::fn;
}


 
ava
volatile | 27.09.2013, 11:10 #
Хех, как будто проблема удаления это главная проблема
Цитата (volatile @  25.9.2013,  18:58 findReferencedText)
вы их правильно удалить даже не сможете, ...

я уж не говорю о чем-то другом.


О чем-то другом еще подумайте, если вы с объктами еще что-то хотите делать, кроме удаления  smile 
ava
BearFear | 27.09.2013, 12:29 #
В первом сообщении еще было о том, что удаление и хранение разных типов в списке является главной проблемой. Если нет потребности в ином, зачем иное прописывать? Чтоб просто так было и плодились ошибки?
ava
BearFear | 27.09.2013, 13:10 #
mes, стало быть тип хранится в статическом коллбэке и сохраняется туда на стадии компиляции. Тип хранимого данного в этот момент не важен. Спасибо большое! Думаю Скептикам вроди volatile есть что поиметь ввиду. Ыть  smile
ava
mes | 27.09.2013, 14:13 #
Цитата (BearFear @  27.9.2013,  12:10 findReferencedText)
стало быть тип хранится в статическом коллбэке и сохраняется туда на стадии компиляции. Тип хранимого данного в этот момент не важен. 
aга, примерно так smile
ava
BearFear | 27.09.2013, 17:15 #
mesvolatileakizelokroxvrbaldina, спасибо вам большое за помощь!
ava
BearFear | 28.09.2013, 03:58 #
Еще одна особенность. Данный метод пригоден для стековых данных. Однако, перегрузка delete все решит.
ava
mes | 28.09.2013, 10:50 #
Цитата (BearFear @  28.9.2013,  02:58 findReferencedText)
 Однако, перегрузка delete все решит. Либо френд

 smile  Однако, выпью чашечку кофе, Либо сбоку..  :crazy

added later:
Цитата (BearFear @  28.9.2013,  02:58 findReferencedText)
Еще одна особенность. Данный метод пригоден для стековых данных.

?  smile 
ava
BearFear | 28.09.2013, 18:48 #
Динамическое создание будет выглядеть немного по иному, не так как было написано mes. Если действовать по канонам только динамических объектов (сокрытие конструктора, деструктора и прочих методов), то удаление может происходить либо через производный класс  и протектед, либо через перегрузку делит, либо через френд.
ava
mes | 28.09.2013, 20:34 #
Цитата (BearFear @  28.9.2013,  17:48 findReferencedText)
Если действовать по канонам только динамических объектов (сокрытие конструктора, деструктора и прочих методов),

о время ! о нравы !
Цитата (BearFear @  28.9.2013,  17:48 findReferencedText)
то удаление может происходить либо через производный класс  и протектед, либо через перегрузку делит, либо через френд. 

мысль  хоть и с трудом, но стала понята...  smile  
однако далека от истины, хотя частичку проблемки отражает..
полагаю вас тянет ознакомиться с SFINAE..  smile 
ava
BearFear | 29.09.2013, 02:12 #
Хм, о таком маневре не знал. Спасибо за подсказку.  Подстановка вызова метода в области параметра. Попробую поискать еще побольше инфы.
ava
mes | 29.09.2013, 10:39 #
Цитата (BearFear @  29.9.2013,  01:12 findReferencedText)
Про методы там не писалось

с методами такая же история smile
ava
BearFear | 29.09.2013, 21:37 #
Тогда ваще ГУД!
Please register or login to write.
Firm of day
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Contributors
advanced
Submit