Не подскажете, как сотворить запрет на создание объектов на стеке.
Уже не превый раз возникает такая необходимость.
Вот пример:
в нашей программе все андушные объекты хранятся в анду-манагере (указатели на них), и естественно просто так взять и грохнуть объект нельзя, можно только сказать ему Delete(), и он пометится в анду-манагере как удаленный, чтобы потом можно было анду-манагеру сказать Undo(), и только что замоченный объект — тут как тут. Если бы мы грохнули объект delete'ом, то к нему уже ясен пень проандушится было бы не возможно, и к тому же в стеке анду-транзакций останется куча ссылок на него, и выколупывать их никто не будет; да и вообще, какое тогда анду, если объект чья история у нас хранится взять и физически грохнуть?
Так вот, при чем тут стековые объекты,- мы-то временем их жизни не управляем — дошли до конца блока и он помер, поэтому наши андушные объекты нельзя создавать на стеке. Сам факт создания андушного объекта есть история анду, которая должна храниться, поэтому при создании такие объекты помещаются в анду-манагер. Анду-манагер сам знает, когда у него нет ссылок на какой-то объект и прибивает его оператором delete, и если подсунуть ему указатель на стековый объект, то хорошего случится мало.
Как запретить создавать объекты некого класса на стеке, я-то знаю — закрываем конструкор и делаем функцию static Object * Create();, которая будет создавать объекты в куче. Но вот проблема с анду — в том, что надо (очень хочется ) запреть в базовом классе создавать [b]наследников[b] на стеке. Наследников у нашего IUndoObject штук 100 не меньше, и всем закрывать конструктор и писать Create() не самый удачный выход, потому что это все равно не проверяется компилятором (ему-то гаду пофиг) — достаточно где-то завтыкать, или написать новый код, в котором забыть про Create, и все — упадешь и не заметишь. Да и вообще — это куча дублирования кода, и так сказать "мантра", которую надо помнить, знать и понимать.
Извините если сумбурно объяснил причины, надеюсь на помощь.
Re: запретить создавать объекты на стеке
От:
Аноним
Дата:
05.07.09 10:08
Оценка:
Здравствуйте, pastey, Вы писали:
class A
{
public:
A();
virtual void Delete()
{
// do somethingdelete this;
}
protected:
~A()
{
// do somesing
}
};
class B : public A
{
public:
B();
protected:
~B()
{
// do something
}
};
void main()
{
A a0; // <- compilation error
A a = new A;
B b = new B;
delete a; // <- compilation error
a->Delete(); // OKdelete b; // <- compilation error
b->Delete(); // OK
}
А>class A
А>{
А>public:
А> A();
А> virtual void Delete()
А> {
А> // do something
А> delete this;
А> }
А>protected:
А> ~A()
А> {
А> // do somesing
А> }
А>};
А>class B : public A
А>{
А>public:
А> B();
А>protected:
А> ~B()
А> {
А> // do something
А> }
А>};
А>void main()
А>{
А> A a0; // <- compilation error
А> A a = new A;
А> B b = new B;
А> delete a; // <- compilation error
a->>Delete(); // OK
А> delete b; // <- compilation error
b->>Delete(); // OK
А>}
А>
спасибо за ответ, это конечно выход, но не без недостатков — в каждом наследнике нужно протектить деструктор, и об этом легко забыть (наследников много, и еще будут добавляться новые — писать гневный коментарий — "делайте детсруктор наследника протекченым" — не самый крутой способ защиты).
заприватить деструктор нельзя, т.к. тогда не скомпилится деструктор наследника, а дружить всех наследников с папашей — смешно.
но все равно — спасибо за ответ, вдруг натолкнет на мысль
Re: запретить создавать объекты на стеке
От:
Аноним
Дата:
05.07.09 11:02
Оценка:
Здравствуйте, pastey, Вы писали:
P>Здраствуйте, Товарищи.
P>Не подскажете, как сотворить запрет на создание объектов на стеке. P>Уже не превый раз возникает такая необходимость.
P>Как запретить создавать объекты некого класса на стеке, я-то знаю — закрываем конструкор и делаем функцию static Object * Create();, которая будет создавать объекты в куче. Но вот проблема с анду — в том, что надо (очень хочется ) запреть в базовом классе создавать [b]наследников[b] на стеке. Наследников у нашего IUndoObject штук 100 не меньше, и всем закрывать конструктор и писать Create() не самый удачный выход, потому что это все равно не проверяется компилятором (ему-то гаду пофиг) — достаточно где-то завтыкать, или написать новый код, в котором забыть про Create, и все — упадешь и не заметишь. Да и вообще — это куча дублирования кода, и так сказать "мантра", которую надо помнить, знать и понимать.
Что мешает использовать templates?
class UndoMngr
{
//...public:
template<typename T>
static T * Create()
{
T * result = new T();
// register Object...return result;
}
//...
};
class IUndoObject
{
//...friend class UndoMngr;
protected:
IUndoObject();
virtual ~IUndoObject();
//...
};
Здравствуйте, pastey, Вы писали:
P>Здраствуйте, Товарищи.
P>Не подскажете, как сотворить запрет на создание объектов на стеке. P>Уже не превый раз возникает такая необходимость.
P>Вот пример: P>в нашей программе все андушные объекты хранятся в анду-манагере (указатели на них), и естественно просто так взять и грохнуть объект нельзя, можно только сказать ему Delete(), и он пометится в анду-манагере как удаленный, чтобы потом можно было анду-манагеру сказать Undo(), и только что замоченный объект — тут как тут. Если бы мы грохнули объект delete'ом, то к нему уже ясен пень проандушится было бы не возможно, и к тому же в стеке анду-транзакций останется куча ссылок на него, и выколупывать их никто не будет; да и вообще, какое тогда анду, если объект чья история у нас хранится взять и физически грохнуть? P>Так вот, при чем тут стековые объекты,- мы-то временем их жизни не управляем — дошли до конца блока и он помер, поэтому наши андушные объекты нельзя создавать на стеке.
Имхо как-то странно. Обратное действие — может быть обычным действием. Т.е. например есть действия "удалить" и "создать". Каждое из них есть обартное действие для другого, поэтому здесь нет разделения 'undo'/'не undo'. Просто есть контекст в котором действие трактуется как 'обратное'.
В первом приближении:
class action
{
public:
virtual void run() = 0;
virtual smart_ptr<action> get_undo_action() = 0;
//...
};
class action_add : public action
{
public:
virtual smart_ptr<action> get_undo_action();
//...
};
class action_remove : public action
{
public:
virtual smart_ptr<action> get_undo_action();
//...
};
smart_ptr<action> action_add::get_undo_action(){
return smart_ptr<action>(new action_remove(some_args));
}
smart_ptr<action> action_remove::get_undo_action(){
return smart_ptr<action>(new action_add(some_args));
}
//...void run(smart_ptr<action> a)
{
a->run();
undo_stack.add(a->get_undo_action());
}
Понятно что могут быть ситуации в которых такой подход не может быть применим, но в любом случае сабжевой проблемы я здесь не вижу.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Имхо как-то странно. Обратное действие — может быть обычным действием. Т.е. например есть действия "удалить" и "создать". Каждое из них есть обартное действие для другого, поэтому здесь нет разделения 'undo'/'не undo'. Просто есть контекст в котором действие трактуется как 'обратное'.
тот код, который ты привел — это командное анду — выполнить операцю такую-то, выполнить операцию обратную ей. наше анду основано на паттерне Memento — все данные объекта хранятся отдельно, и каждая операция с объектом создает новое хранилище данных, которое сохраняет его текущее состояние в стек анду. Вобщем-то к сабжу это действитеьно не имеет отношения.
Re: запретить создавать объекты на стеке
От:
Аноним
Дата:
05.07.09 11:53
Оценка:
под винду както так:
class foo
{
foo()
{
volatile int test = 0;
VirtualQuery(&test, ... &mbi1,...);
VirtualQuery(this, ... &mbi2,...);
assert (mbi1.AllocationBase!=mbi2. AllocationBase);
}
};
Здравствуйте, Аноним, Вы писали:
А>Что мешает использовать templates?
А>
А>class UndoMngr
А>{
А>//...
А>public:
А>template<typename T>
А>static T * Create()
А>{
А>T * result = new T();
А>// register Object...
А>return result;
А>}
А>//...
А>};
А>class IUndoObject
А>{
А>//...
А>friend class UndoMngr;
А>protected:
А>IUndoObject();
А>virtual ~IUndoObject();
А>//...
А>};
А>
я вот тоже думаю о каком-то шаблоне. твой явно задачу не решает, т.к. ничего не мешает в наследнике IUndoObject сделать открытый конструктор и деструктор:
class CBadUndoObject : public IUndoObject
{
public:
CBadUndoObject() { /* some code */ }
~CBadUndoObject() { /* some code */ }
};
/* где-то в коде */
{
pUndoMgr->BeginGroup();
CBadUndoObject aBadUndoObject( pUndoMgr ); /* тут он за счет конструктора базового анду-объекта регится в анду-манагере */
pUndoMgr->EndGroup(); /* в конце транзакции создание объекта регистрируется и засовывается в стек анду */
} /* тут 'aBadUndoObject' доблестно погибает, и даже разрегестрируется из анду-манагера, но потроха его остаются в стеке анду */
... где-то дальше в коде
void OnUndo()
{
pUndoMgr->Undo(); /* и тут мы полезем в стек анду, вытащим указатель на 'aBadUndoObject' и попытаемся вернуть ему его прошлое состояние (в этом примере - состояние несуществования :) ), и на этой попытке и помрем, т.к. обратимся к указателю на мертвый объект*/
}
вобщем, проблема не в анду-манагере, ну просто нельзя создавать андушный объект на стеке. представь — пользователь создает объект с помощью программы, ведь не может он прожить в пределах одной функции, он должен жить пока пользователь не скажет его удалить. логично, что это будет не стековый объект — временем его жизни ты так не по-управляешь. это я не к твоему коду, а так — всем.
забудьте о том, что тут было анду — проблема такая — "запретить создавать наследников некоего интерфеса на стеке средствами этого самого интерфеса ( или еще как-нибудь, может подмешивать еще какой-то базовый класс, я не знаю... )"
Здравствуйте, pastey, Вы писали:
P>Извините если сумбурно объяснил причины, надеюсь на помощь.
Если я верно понял, что тебе надо, то могу предложить такую схему.
1) В корневой класс ваших undo-объектов, добавляешь шаблон метода, который создаёт наследника.
2) В реальности эта штука должна создавать не заявленного наследника, а шаблонного наследника от него.
3) В корневом классе заводишь pure virtual метод, который сторонний код не может перекрыть (например там можно заюзать private типы).
То есть как-то так, примерно:
class CUndoObject {
struct SecretSeed {};
template<typename T> class Placeholder : public T { void checkSecretSeed( const SecretSeed& ) {} };
public:
template<typename T>
static T* CreateNew() { return new Placeholder<T>;
private:
virtual void checkSecretSeed( const SecretSeed& ) = 0;
};
Вторая стратегия -- проверка в rt.
1) перекрываем у CUndoObject operator new
2) внутри него регим все аллокируемые блоки
3) в конструкторе кастимся по dynamic_cast к void* и смотрим есть ли такой блок в базе.
Если есть -- то вычёркиваем. Если нет -- ругаемся...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Аноним, Вы писали:
А>под винду както так:
Это не решит проблемы топикстартера.
Например, создавать объект, как поле другого объекта, тоже нельзя...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, pastey, Вы писали:
P>>Извините если сумбурно объяснил причины, надеюсь на помощь.
E>Если я верно понял, что тебе надо, то могу предложить такую схему.
E>1) В корневой класс ваших undo-объектов, добавляешь шаблон метода, который создаёт наследника. E>2) В реальности эта штука должна создавать не заявленного наследника, а шаблонного наследника от него. E>3) В корневом классе заводишь pure virtual метод, который сторонний код не может перекрыть (например там можно заюзать private типы).
E>Вторая стратегия -- проверка в rt. E>1) перекрываем у CUndoObject operator new E>2) внутри него регим все аллокируемые блоки E>3) в конструкторе кастимся по dynamic_cast к void* и смотрим есть ли такой блок в базе. E>Если есть -- то вычёркиваем. Если нет -- ругаемся...
Супер — спасибо большое — выглядит очень классно, особенно первое.
Второе тоже ничего, но на ран-тайм откладывать не хочется — пропустим какой-то редко используемы случай и кирдык.
Завтра испытаю — отпишусь, сейчас под рукой нет компилятора.
Еще раз спасибо большое.
И так — ради прикола — на днях посетила мысля, как на плюсах файнал класс сделать — приватим конструктор, открываем CreateNew() и готово — попробуй отнаследуйся; может и баян, но я сам придумал
код должен быть кроссовый, но все равно спасибо — узнал про VirtualQuery — выглядит ужасно, но прикольно
А>а вообще — shared_ptr в руки и все.
shared_ptr тем более проблемы не решит. он мне не запретит создать отдельно объекты на стеке.
запретить дианамически грохать объекты кому-нипопадя кроме манагера я могу — закрыть delete анду-объекту и подружить его с манагером
Здравствуйте, pastey, Вы писали:
P>Завтра испытаю — отпишусь, сейчас под рукой нет компилятора.
У меня, к сожалению, прямо сейчас тоже нет...
P>Еще раз спасибо большое.
Для "спасибо" тут есть кнопки
P>И так — ради прикола — на днях посетила мысля, как на плюсах файнал класс сделать — приватим конструктор, открываем CreateNew() и готово — попробуй отнаследуйся; может и баян, но я сам придумал
Ну ещё можно через деструктор примерно так же читить...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: запретить создавать объекты на стеке
От:
Аноним
Дата:
05.07.09 13:34
Оценка:
P>shared_ptr тем более проблемы не решит. он мне не запретит создать отдельно объекты на стеке.
Нупо крайней мере такая декларация метода
void AddObject(std::shared_ptr<Object> &obj);
ясно скажет вызываюшему, что скармливать ему объекты которые удаляются чем либо кроме самого шаредптр — ошибка.
P>запретить дианамически грохать объекты кому-нипопадя кроме манагера я могу — закрыть delete анду-объекту и подружить его с манагером
дружба — это нехорошо
Здравствуйте, pastey, Вы писали:
P>Супер — спасибо большое — выглядит очень классно, особенно первое. P>Второе тоже ничего, но на ран-тайм откладывать не хочется — пропустим какой-то редко используемы случай и кирдык. P>Завтра испытаю — отпишусь, сейчас под рукой нет компилятора.
// Объект, который нельзя создать, иначе чем встроенной в него фабрикой...class IDynCreatedObject {
public:
virtual ~IDynCreatedObject() {}
template<typename TDynCreatedObject> static TDynCreatedObject* CreateNew()
{ return new objectPlaceholder<TDynCreatedObject>; }
template<typename TDynCreatedObject> static TDynCreatedObject* CreateCopyOf( const TDynCreatedObject& toClone )
{ return new objectPlaceholder<TDynCreatedObject>( toClone ); }
template<typename TDynCreatedObject> static TDynCreatedObject* CreateNew( TDynCreatedObject*& toInit )
{ return toInit = CreateNew<TDynCreatedObject>(); }
private:
class secretSeed {};
virtual secretSeed checkSecretSeed( secretSeed ) = 0;
template<typename TDynCreatedObject>
struct objectPlaceholder : TDynCreatedObject {
objectPlaceholder() {}
objectPlaceholder( const TDynCreatedObject& other ) : TDynCreatedObject( other ) {}
secretSeed checkSecretSeed( secretSeed ss ) { return ss; }
};
};
#endif//!DynCreatedObject_h
это VC2008 типа.
пример использования:
struct testDCO : IDynCreatedObject {
// secretSeed checkSecretSeed( secretSeed ss ) { return ss; }
};
struct testTestDCO {
// testDCO forbiddenField;
};
void testIDynCreatedObject ()
{
// delete new testDCO;
// testDCO autoObj;
testDCO* obj = testDCO::CreateNew(obj); // Тут нет UB, так как возврат из функции -- точка следования!delete obj->CreateCopyOf( *obj );
delete obj;
}
закомментированные строчки не компилируются...
В принципе, возможно, будет удобно сразу скрестить это с этим
...
Но это уже как приложится и что на самом деле нужно
Пиши в общем чего и в какой форме пригодилось...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском