Отслеживание автоматической переменной времени жизни? - PullRequest
1 голос
/ 17 октября 2008

Это может быть невозможно, но я решил спросить ...

Может ли кто-нибудь придумать, отслеживать ли удаленную автоматическую переменную без изменения класса самой переменной? Например, рассмотрим этот код:

const char* pStringBuffer;
{
    std::string sString( "foo" );
    pStringBuffer = sString.c_str();
}

Очевидно, что после блока pStringBuffer является висящим указателем, который может быть или не быть действительным. Я хотел бы иметь способ иметь класс-оболочку, который содержит pStringBuffer (с оператором приведения для const char *), но утверждает, что переменная, на которую он ссылается, все еще действительна. Изменяя тип ссылочной переменной, я, безусловно, могу это сделать (например, boost shared_ptr / weak_ptr), но я бы хотел сделать это без наложения ограничений на ссылочный тип.

Некоторые мысли:

  • Мне, вероятно, понадобится изменить синтаксис присваивания, чтобы включить в него переменную, на которую ссылаются (это нормально)
  • Возможно, я смогу взглянуть на указатель стека, чтобы определить, был ли мой класс-оболочка выделен «позже», чем указанный класс, но это выглядит странно и нестандартно (C ++ не определяет поведение стека). Это могло бы работать, хотя.

Мысли / блестящие решения?

Ответы [ 4 ]

1 голос
/ 17 октября 2008

В общем, это просто невозможно из C ++, так как указатели слишком «сырые». Кроме того, проверка того, что вы были размещены позже, чем указанный класс, не сработает, потому что, если вы измените строку, указатель c_str вполне может измениться.

В этом конкретном случае вы можете проверить, возвращает ли строка то же значение для c_str. Если это так, вы, вероятно, все еще действительны, а если нет, то у вас есть недопустимый указатель.

В качестве инструмента отладки я бы посоветовал использовать усовершенствованную систему отслеживания памяти, такую ​​как valgrind (боюсь, она доступна только для Linux. Подобные программы существуют для Windows, но я считаю, что все они стоят денег. Эта программа - единственная причина, по которой я на моем Mac установлен linux). За счет гораздо более медленного выполнения вашей программы valgrind обнаруживает, читали ли вы когда-нибудь недопустимый указатель. Хотя он не идеален, я обнаружил, что он обнаруживает много ошибок, в частности, такого типа.

0 голосов
/ 17 октября 2008

Учитывая, что "pStringBuffer" является единственной частью вашего примера, существующей после того, как sString выходит из области видимости, вам нужно внести в нее некоторые изменения или замену, которая отразит это. Один простой механизм - это своего рода защита области видимости с соответствием области sString, которая влияет на pStringBuffer при его уничтожении. Например, он может установить для pStringBuffer значение NULL.

Сделать это без изменения класса «переменной» можно только несколькими способами:

  • Введите отдельную переменную в той же области, что и sString (чтобы уменьшить многословность, вы можете рассмотреть макрос для генерации двух вещей вместе). Не приятно.

  • Wrap с шаблоном ала X sString: это спорно ли это «изменение типа переменной» ... альтернативная перспектива, что sString становится обертку вокруг одной и той же переменной. Кроме того, страдает то, что лучшее, что вы можете сделать - это передать аргументы шаблонного конструктора обернутым конструкторам вплоть до некоторых конечных аргументов N.

Ничто из этого не сильно помогает, поскольку разработчик не забывает их использовать.

A намного лучший подход состоит в том, чтобы сделать "const char * pStringBuffer" просто "std :: string some_meaningful_name" и назначить его при необходимости. Учитывая подсчет ссылок, это не слишком дорого в 99,99% случаев.

0 голосов
/ 17 октября 2008

Вы можете создать класс-оболочку, который будет работать в том простом случае, который вы упомянули. Может быть, что-то вроде этого:

X<const char*> pStringBuffer;
{
    std::string sString( "foo" );
    Trick trick(pStringBuffer).set(sString.c_str());
} // trick goes out of scope; its destructor marks pStringBuffer as invalid

Но это не помогает более сложным случаям:

X<const char*> pStringBuffer;
{
    std::string sString( "foo" );
    {
        Trick trick(pStringBuffer).set(sString.c_str());
    } // trick goes out of scope; its destructor marks pStringBuffer as invalid
}

Здесь, аннулирование происходит слишком рано.

В основном вы должны просто написать код, который был бы максимально безопасным (см .: интеллектуальные указатели), но не более безопасным (см .: производительность, низкоуровневые интерфейсы), и использовать инструменты (valgrind, Purify), чтобы убедиться, что ничего не проскальзывает трещины.

0 голосов
/ 17 октября 2008

Один из методов, который вам может пригодиться, состоит в замене операторов new / delete вашими собственными реализациями, которые помечают используемые страницы памяти (выделенные вашим operator new) как недоступные при освобождении (освобождаемые вашим *). 1004 *). Вы должны будете убедиться, что страницы памяти никогда не используются повторно, поэтому будут ограничения в отношении продолжительности времени выполнения из-за исчерпания памяти.

Если ваше приложение обращается к страницам памяти после того, как они были освобождены, как в вашем примере выше, ОС перехватит попытку доступа и выдаст ошибку. Это не совсем отслеживание само по себе, поскольку приложение будет немедленно остановлено, но оно обеспечивает обратную связь: -)

Эта методика применима в узких сценариях и не учитывает все типы злоупотреблений памятью, но может быть полезна. Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...