Велосипед: Полуумный указатель
Так как мне нужно выполнение только основной логики, решил сделать велосипед, реализующий только основное назначение shared_ptr (вызывать delete, когда объект никому не нужен). Если кому не лень, посмотрите сильно ли кривой вышел велосипед.
class Half_Ptr
{
struct HalfObject // прилепляет к объекту счетчик указателей
{
unsigned int counter;
T *targetObject;
HalfObject(T *tOb):counter(0), targetObject(tOb){}
virtual ~HalfObject()
{
// cout << "ksObject deleted" << endl; - проверка
delete targetObject;
}
};
HalfObject *objPtr;
void DecPtr()
{
if (objPtr)
{
objPtr->counter--;
if (!objPtr->counter)
delete objPtr;
}
}
public:
Half_Ptr():objPtr(0)
{
}
~Half_Ptr()
{
DecPtr();
}
Half_Ptr(T* ref)
{
objPtr = new HalfObject(ref);
if (objPtr)
objPtr->counter++;
}
Half_Ptr(const Half_Ptr<T>& ref)
{
objPtr = ref.objPtr;
if (objPtr)
objPtr->counter++;
}
Half_Ptr operator=(const Half_Ptr<T>& ref)
{
DecPtr();
objPtr = ref.objPtr;
if (objPtr)
objPtr->counter++;
return *this;
}
T* operator->()
{
return this->objPtr->targetObject;
}
// для проверки
unsigned GetCounter()
{
if (objPtr) return objPtr->counter;
return 0;
}
};
// Проверка работы
struct TestObject
{
int m;
TestObject(int m):m(m)
{
}
~TestObject()
{
cout << "TestObject deleted: " << m << endl;
}
};
int main()
{
typedef Half_Ptr<TestObject> refType;
refType myPtr3;
{
refType myPtr(new TestObject(20));
cout << myPtr->m << endl;
cout << "Counter: " << myPtr.GetCounter() << endl;
refType myPtr2 = myPtr;
cout << myPtr2->m << endl;
cout << "Counter: " << myPtr2.GetCounter() << endl;
myPtr3 = myPtr2;
}
cout << myPtr3->m << endl;
cout << "Counter: " << myPtr3.GetCounter() << endl;
{
vector<refType > myVector;
myVector.push_back(new TestObject(1));
myVector.push_back(new TestObject(2));
myVector.push_back(new TestObject(3));
myVector.push_back(new TestObject(4));
myVector.push_back(new TestObject(5));
myPtr3 = myVector[0];
for (size_t i = 0; i < myVector.size(); ++i)
{
cout << myVector->m << endl;
}
}
{
vector<refType > myVector(5, refType(new TestObject(77)));
myVector[0]->m++;
for (size_t i = 0; i < myVector.size(); ++i)
{
cout << myVector->m << endl; // 78 для всех - объект один
}
}
cout << myPtr3->m << endl;
return 0;
}
[quote=ВсемИзвестныйФильм]Не скрою, это от души. Ложки у меня пациенты много раз глотали, но чтобы вот так -- за обедом и острый предмет... За это Вам наше искреннее мерси. Ежели, конечно, кроме железных предметов, ещё и фарфор можете употребить, тогда... Слов нет.[/quote]
Я хотел сказать: а что-то аналогичное для массивов? Т.е. чтобы память выделялась при создании контейнера на указанное количество элементов и освобождалась, когда разрушается последний экземпляр контейнера.
PS. Отладка методом пристального всматривания косяков не обнаружила.
Есть boost::shared_array, boost::scoped_array, но мне они пока не нужны. Когда нужных фич из boost станет много - то просто добавлю boost.
Моя подобная отладка обнаружила пару сомнительных мест.
Тут вроде бы проверка лишняя. Правильнее исключение ловить.
if (objPtr)
Ну и как-то странно, что компилятор переварил, что класс является сам себе другом (friend) по умолчанию. То есть один объект свободно обращается к закрытым полям объекта этого же класса. Хотя, может так и должно быть.
Так ведь сам предложил по возможности без boost обойтись.
if (objPtr)
Это в конструкторе? Имхо, пусть программист, использующий твою библиотеку, сам думает, что он конструктору передаёт.
Саму себе в друзья набиваться, конечно, бессмысленно, но не противоречит синтаксису и семантике оператора friend. А я, кстати, этого в коде и не заметил... Да, спать... спать...
Стратегия такая: у меня есть "походная" IDE, которую я могу быстро поставить на разные компьютеры, чтобы отлаживать свои приложения. Поэтому стараюсь по возможности не использовать библиотек, которые не поставляются вместе с компилятором. Может, зря.
Возможно, стоит собрать свой дистрибутив IDE на основе Code::Blocks, чтобы не мучиться.
Хм. Мою библиотеку... Тут нужен то всего один хедер. Хотя, я и до этого простые куски из Boost вытаскивал. Скоро издам микроБуст :)
Или не выдержу и пойду осваивать какой-нибудь D...
Он самый. :)
Лучше J. :D
Что толку от этого J ? - только поиграться. Да и вообще - проприетарщина какая-то. Для поиграться у меня Python есть. Про D я агитации прочитал - вроде красиво. Примерно то же, что и C++, но там такой велосипед не нужен. А что на деле - не знаю, надо будет как-нибудь проверить.
Кошмарный жэсть это. Авторы языка понапихали в язык всего чего только знали... Уродство вышло, имхо.
Имхо, для обработки всяких массивов информации J -- самое то. Хотя, есть ещё MatLab.
Читал. Правда через строчку и не все главы... Некоторые главы устарелые. Некоторые я просто не смог переварить - не дорос наверное.
{
virtual void Inc() = 0;
virtual int Dec() = 0;
virtual void* Get() = 0;
virtual int GetCounter() = 0;
virtual ~HalfBase(){cout << "HalfBase deleted" << endl;}
};
template<class T>
class Half_Ptr
{
struct HalfObject: public HalfBase // This one must add counter of pointers to object
{
unsigned int counter;
T *targetObject;
HalfObject(T *tOb):counter(0), targetObject(tOb){}
void Inc()
{
++counter;
}
int Dec()
{
return --counter;
}
void* Get()
{
return targetObject;
}
int GetCounter()
{
return counter;
}
~HalfObject()
{
cout << "HalfObject deleted" << endl; //- old test
delete targetObject;
}
};
public:
HalfBase *objPtr;
void DecPtr()
{
if (objPtr)
{
if (!objPtr->Dec())
delete objPtr;
}
}
Half_Ptr():objPtr(0)
{
}
~Half_Ptr()
{
DecPtr();
}
Half_Ptr(T* ref)
{
objPtr = new HalfObject(ref);
objPtr->Inc();
}
Half_Ptr(const Half_Ptr<T>& ref)
{
objPtr = ref.objPtr;
if (objPtr)
objPtr->Inc();
}
template<class F>
Half_Ptr operator=(const Half_Ptr<F>& ref)
{
DecPtr();
objPtr = ref.objPtr;
if (objPtr)
objPtr->Inc();
return *this;
}
Half_Ptr operator=(const Half_Ptr<T>& ref)
{
DecPtr();
objPtr = ref.objPtr;
if (objPtr)
objPtr->Inc();
return *this;
}
T* operator->()
{
return static_cast<T*>( this->objPtr->Get());
}
// for test
unsigned GetCounter()
{
if (objPtr) return objPtr->GetCounter();
return 0;
}
};
// Проверка работы
struct TestBase
{
virtual void print() = 0;
virtual ~TestBase(){cout << "TestBase deleted" << endl;}
};
struct TestObject1: public TestBase
{
int m;
void print()
{
cout << "TestObject1.m: " << m << endl;
}
TestObject1(int m):m(m)
{
}
~TestObject1()
{
cout << "TestObject1 deleted: " << m << endl;
}
};
struct TestObject2: public TestBase
{
int m;
void print()
{
cout << "TestObject2.m^2: " << m*m << endl;
}
TestObject2(int m):m(m)
{
}
~TestObject2()
{
cout << "TestObject2 deleted: " << m << endl;
}
};
int main()
{
Half_Ptr<TestBase> myBasePtr;
{
Half_Ptr<TestObject1> myPtr(new TestObject1(20));
myBasePtr = myPtr;
myPtr->print();
}
myBasePtr->print();
cout << endl;
{
Half_Ptr<TestObject2> myPtr(new TestObject2(30));
myBasePtr = myPtr;
myPtr->print();
}
myBasePtr->print();
return 0;
}
Факт. Да и не только массивов.
Что значит "выколупывать"?
Достаточно одной строки:
using namespace boost; // необязательно
...
shared_ptr<MyClass> ptr; // или boost::shared_ptr<MyClass> ptr, если не хотите использовать using namespace
Помнится, как-то так... К сожалению, нет студии под рукой.
Может, ваша ошибка в том и состоит, что вы не поняли, как с бустом работать?
Достаточно одной строки:
using namespace boost; // необязательно
...
shared_ptr<MyClass> ptr; // или boost::shared_ptr<MyClass> ptr, если не хотите использовать using namespace
Это все понятно.
Мне нужна маленькая "походная" IDE, которую я могу использовать на случайном компьютере. Ну, пусть в нем стоит Windows 2000/XP/Wista. Сейчас я могу за полминуты установить IDE и работать.
Складывать в нее более 300 мегабайт непонятного кода неохота. Зачем мне оно надо, если требуются только умные указатели?
Есть конечно утилита boost bcp, но это решение не очень элегантное. Я посмотрел состав компилятора gcc 4.4.0 - туда уже включили эти указатели, чтобы соответствовать грядущему стандарту. Буду копать в сторону использования этого компилятора.
Складывать в нее более 300 мегабайт непонятного кода неохота. Зачем мне оно надо, если требуются только умные указатели?
Лично мой буст 1.33.0 (может, я и отстал от жизни, но вряд ли он в разы вырос), я измерил вес, получил менее 100М. А подпапка boost (без doc, lib и т. д. - они не нужны) около 20М.
Не проще ли заархивировать её и носить на флэшке, распаковывая напрямик в папку инклюдов вашего IDE :)
А если вам и 20 метров не нравится, то из boost выбросьте все подпапки (не файлы), кроме detail и config, у меня после такого хирургического вмешательства осталось 1,3 м и все виды умных указателей прекрасно компилируются. Если что непонятно, вложил скриншот :)
Да всё это выясняется экспериментальным путём, могли бы и сами выяснить :)
Весит 608 Кб
Не проще ли заархивировать её и носить на флэшке, распаковывая напрямик в папку инклюдов вашего IDE :)
А если вам и 20 метров не нравится, то из boost выбросьте все подпапки (не файлы), кроме detail и config, у меня после такого хирургического вмешательства осталось 1,3 м и все виды умных указателей прекрасно компилируются. Если что непонятно, вложил скриншот :)
Согласен с Kogrom. Если boost в разработке обычно не используется, не вижу смысла использовать ее только из-за одного класса.
А вот я несогласен, потому что
1) К велосипедостроению всегда отношусь плохо, разве что с целью самообразования и тренировки
2) буст кроссплатформенный, и будет под любым IDE работать, тем более, что автор упомянул "случайный компьютер"
3) буст проверен многими годами, а самописный код глюкнет на "случайном компьютере" и сиди разбирайся
4) буст собсно создан как совокупность многих библиотечек, а не как одна жирная Библия (верующие да не сочтят за оскорбление) именно для того, чтобы каждую отдельную возможность ("библиотеку") можно было использовать независимо от остальных, а конкретные разжёванные рекомендации по волшебному похудению даны чуть выше
Да всё это выясняется экспериментальным путём, могли бы и сами выяснить :)
Весит 608 Кб
Ну, может надо посмотреть в старой библиотеке. В той, что у меня в хедере shared_ptr.hpp кроме сторожа включения и комментариев только одна строчка:
#include <boost/smart_ptr/shared_ptr.hpp>
то есть придется еще папки включать. Но вообще, идея понятна.
Добавлено позже. Мне тоже идея с велосипедом не нравится. Но всё же лучше я попытаюсь добыть умный указатель из новых версий компиляторов.