Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > существование объекта


Автор: _hunter 25.11.2003, 12:09
Как проверить существование объекта?
( в Delphi была функция assigned() )

Заранее спасибо за ответ.

Автор: shedon 25.11.2003, 12:14
можно и так: ASSERT_VALID(pOb)

Автор: Alexander777 27.4.2007, 00:57
Но ASSERT_VALID прерывает выполнение программы и выдаёт сообщение. А как проверить существование объекта без прерывания программы?

Автор: Daevaorn 27.4.2007, 01:27
Цитата(Alexander777 @  27.4.2007,  01:57 Найти цитируемый пост)
А как проверить существование объекта без прерывания программы?

Какого именно объекта?
Объект на стеке:
Код

class Foo
{
//...
};

//...
Foo bar;
//здесь уже объект существует.
 
В динамической памяти сложнее. Можно определить сразу после создания
Код

Foo* bar = new Foo;
//здесь уже объект существует.


Цитата(_hunter @  25.11.2003,  13:09 Найти цитируемый пост)
 в Delphi была функция assigned() 

в С++ и Delphi "разные" объекты. В Delphi все объекты(инстансы класса) ссылки, поэтому там и есть эта функция.

Автор: Earnest 27.4.2007, 17:17
В общем случае - никак.
Можно попробовать завести в объекте спец. поле, заполнять его спец. значением, а на деструкторе чистить, и надяться, что "случайно" в этой памяти нужное значение не окажется. Но всегда есть риск, обратившись к освобожденной памяти, получить исключение. Опять же, это вовсе не обязательно.

Если знать такое критически необходимо, нужно, чтобы объекты (конкретного класса или ветви иерархии) сами себя регистрировали. Например:

Код

// объявление:
class WiseObj
{
public:
   WiseObj()   { Register (this); }
  ~WiseObj() { Unregister (this); }

   static bool IsValid (const WiseObj*);

private:
   static void Register();
   static void Unregister();
};

// реализация:
static std::set<WiseObj*> _Registry;

void WiseObj::Register (WiseObj* pObj)
{
   _Registry.insert (pObj);
}

void WiseObj::Unregister (WiseObj* pObj)
{
   _Registry.erase(pObj);
}

bool WiseObj::IsValid (const WiseObj* pObj)
{
   return _Registry.find (pObj) != _Registry.end();
}


Что-то типа этого.

Добавлено через 2 минуты и 58 секунд
Естественно, регистрировать себя дожны все конструкторы, включая конструктор копирования (если нужен). Т.е. никаких генерируемых компилятором конструкторов. Кроме того, реестр лучше сделать не статиком, а синглетоном, чтобы не зависеть от момента его инициализации.

Автор: Ken 28.4.2007, 00:07
Кажется assigned () в делфи просто проверяет является ли указатель не-nil  или нет. То же самое вы можете делать в С++, если при удалении всегда будете присвоить 0 на указатель. Более элегантное решение: умные указатели.

Автор: HappyLife 28.4.2007, 09:30
Хотел сказать тоже самое.
Присвайивайте при объявление указателя, нуль и при удалении нуль.

Автор: Rockie 28.4.2007, 14:42
Цитата(HappyLife @  28.4.2007,  09:30 Найти цитируемый пост)
Присвайивайте при объявление указателя, нуль и при удалении нуль.

+1

Добавлено через 1 минуту и 34 секунды
Ken, +1 smile


Автор: Любитель 28.4.2007, 17:34
Собсно дельфи это делает. Сброс в nil идёт на уровне компилера. Правда это верно для объектов, а вот для произвольных динамических данных - отнюдь нет.

Автор: Ken 28.4.2007, 17:51
Вы можете легко релизовать такое поведение в С++, например через макросы: 
Код

#define assigned(ptr) (ptr == NULL)
#define deleteImproved(ptr) { delete ptr; ptr = NULL; }


Тогда для удаления объекта можете написать:
Код

deleteImproved (foo);


Макропроцессор будет генерировать:
Код

{ delete foo; foo = NULL; }


И assigned будет работать как в дельфи.

Автор: mr.Anderson 28.4.2007, 17:54
Код

class Foo {
 ...
};
//------------------------------------

Foo myObj;

...

if( myObj != NULL )
 //объект существует
else //не существует

Автор: Daevaorn 28.4.2007, 17:59
mr.Anderson, уверен?

Автор: JackYF 28.4.2007, 17:59
Цитата(mr.Anderson @  28.4.2007,  17:54 Найти цитируемый пост)
if( myObj != NULL )

В этом коде не выполнится никогда. См. посты выше. Объект на стеке всегда существует до времени выхода из области видимости.

Автор: Любитель 28.4.2007, 18:06
Да не то что не выполнится - скорее всего и не скомпилится. Если мы сравнение/приведение не перегружали.

Автор: Xenon 28.4.2007, 18:15
И такой код тоже не годится? Тут, по идее, всегда будет выполнятся условие?
Код

Foo* pTr;
pTr = new myObj;
if( myObj != 0 ) ...

Автор: JackYF 28.4.2007, 18:20
Цитата(Любитель @  28.4.2007,  18:06 Найти цитируемый пост)
скорее всего и не скомпилится.

Да, точно. Еще лучше smile

Автор: Любитель 28.4.2007, 18:21
Xenon, тоже не скомпилится. Из " = new myObj" => myObj - это класс. Проверять класс на нулёвость - подозрительно.

А суть в том, что если в плюсах мы что-то явно не инициализировали, то его значение неизвестно (если сие - не объект - для него есть конструктор). Указатель не исключение. Он может быть ненулевым, но эта область памяти будет недоступна. Это приведёт к вылету приложения (юзанье этой памяти). На некоторых компилерах возможен экзепшен, но это платформо- и компилеро- зависимо.

Автор: Xenon 28.4.2007, 18:22
Тьфу, пардон, я имел ввиду
Код

Foo* pTr;
pTr = new Foo;
if( pTr == 0 ) std::cout << "Error";

Error нам никогда не скажут?

copy-paste делать не умею smile

Автор: JackYF 28.4.2007, 18:26
Цитата(Xenon @  28.4.2007,  18:22 Найти цитируемый пост)
Error нам никогда не скажут?

Не-а. Иначе было бы так просто следить за динамической памятью...  smile 

Автор: Любитель 28.4.2007, 18:28
Если выделение удалось - нет (точно), если нет, то вообще говоря будет выброшен экзепшен (по стандарту). На практике - тоже компилерозависимо (читаем Саттера по этому поводу).

Автор: Sartorius 28.4.2007, 18:31
Код

new(nothrow) Foo();


 можно - тогда эксепшона не будет и гарантирован Null при неудаче  smile 

Автор: Любитель 28.4.2007, 18:34
1. Я говорил конкретно про представленный код.
2. 
Цитата(Sartorius @  28.4.2007,  18:31 Найти цитируемый пост)
гарантирован

Тоже нет. Здесь я говорю не от себя (у меня нехватки памяти почему-то никогда не было :( ), но вроде как не все оси могу сие на самом деле гарантировать. На память не скажу (боюсь ошибиться), но что-то по этому поводу в "Новые сложные задачи на C++" было.

Автор: JackYF 28.4.2007, 18:34
Цитата(Sartorius @  28.4.2007,  18:31 Найти цитируемый пост)
можно - тогда эксепшона не будет и гарантирован Null при неудаче  smile 

Да, но это половинчатое решение.
Код

delete pTr; 


не обязан приводить указатель к 0.

Автор: Xenon 28.4.2007, 18:37
JackYF, В смысле "не обязан"? Он это вообще делает, но при сбое не делает?
Вообще вроде как бы по стандарту деструктор объектов не должен вызывать исключения, поэтому сбоя при удалении мы не должны заметить, поэтому если delete обнуляет, то обнулить он должен в любом случае?

Добавлено @ 18:38
Любитель, а что где это у Саттера написано? В 101 правиле или в какой-то из Сложных Задач?

Автор: Mayk 28.4.2007, 19:14
Цитата(Любитель @  28.4.2007,  22:34 Найти цитируемый пост)
На память не скажу (боюсь ошибиться), но что-то по этому поводу в "Новые сложные задачи на C++" было.

Было сказано забить на проверку выделения памяти. Саттер указавал на то, что даже если
new/malloc завершился с успехом, это вовсе не означает что память была действительно выделена. В частности указывалось на
Цитата(man malloc)

      Linux follows an optimistic memory  allocation  strategy.   This  means
       that when malloc() returns non-NULL there is no guarantee that the mem‐
       ory really is available. In case it turns out that the system is out of
       memory,  one  or  more  processes  will  be  killed by the infamous OOM
       killer.


Добавлено @ 19:18
Цитата(Xenon @  28.4.2007,  22:37 Найти цитируемый пост)

Любитель, а что где это у Саттера написано? В 101 правиле или в какой-то из Сложных Задач?

Новые 40 задач. задача 23-часть 2

Цитата

Рекомендация. Мораль #1: избегайте использование оператора new, не генерируюшего исключения.
...
Рекомендация  Мораль #2: очень часто проверка отказа в операторе new бесполезна

Автор: Xenon 28.4.2007, 19:31
Mayk, а что есть выход? ловить bad_alloc?

Автор: Daevaorn 28.4.2007, 19:47
Цитата(Xenon @  28.4.2007,  20:31 Найти цитируемый пост)
 а что есть выход? ловить bad_alloc?

не делать ничего. смысла практически нет.

Автор: Earnest 28.4.2007, 20:02
Цитата(Daevaorn @  28.4.2007,  20:47 Найти цитируемый пост)
не делать ничего. смысла практически нет. 

Да уж, если не найдется памяти для маленького объекта, то можно спокойно рушить приложение: вряд ли уже что-то удастся спасти, если такие дела пошли... 
Это конечно если приложение - не какой-нибудь там кардио-стимулятор... Так их и писать по другому надо...

А "большие" куски памяти лучше выделять не в куче, а прямыми системными вызовами, скажем, в Виндоус - через VirtualAlloc,  и проверять, конечно.

Автор: JackYF 29.4.2007, 23:29
Цитата(Earnest @  28.4.2007,  20:02 Найти цитируемый пост)
скажем, в Виндоус - через VirtualAlloc,  и проверять, конечно.


Хм... и часто ты такое делала?
Не многовато издержек будет на такие вот дела? Или оборачивать еще в оберточки...

Автор: Earnest 2.5.2007, 08:15
Цитата(JackYF @  30.4.2007,  00:29 Найти цитируемый пост)
Хм... и часто ты такое делала?

Всегда, когда большие куски нужны. Еще и с возможным использованием файл-маппинга, если совсем большая память нужна (под какой-нибудь мега-растр). Естественно, с обертками, которые генерируют исключение, и подальше от клиентского кода.
Каких издержек? Это самый что ни есть прямой системный вызов: хип в конце концов тоже так выделяется.
Единственные издержки - округление на размер страницы, но при выделяемых размерах - это мелочь.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)