2012-05-08 2 views
4

Я написал проект, используя обычные указатели, и теперь я устал от ручного управления памятью.Код для рефакторинга для использования общих указателей Boost

Каковы проблемы, которые можно было бы ожидать во время рефакторинга?

До сих пор я уже потратил час, заменяя на shared_ptr<X> для типов, которые я хочу автоматически управлять памятью. Затем я изменил dynamic_cast на dynamic_pointer_cast. Я все еще вижу много ошибок (по сравнению с NULL, передавая функции this).

Я знаю, что вопрос немного расплывчатый и субъективный, но я думаю, что могу воспользоваться опытом того, кто уже это сделал.

Есть ли какие-то подводные камни?

+1

Существует так размещаете на это: http://stackoverflow.com/questions/8334886/c11-smart- указатели-политики, которые проходят через различные типы интеллектуальных ptr и релевантность для C++ 11, если это вас интересует – EdChum

+0

Если у вас так много указателей, вы можете пересмотреть свой стиль кодирования, чтобы увидеть, будут ли автоматические объекты не более подходящее. Иногда люди используют динамическое распределение гораздо больше, чем должны. –

+0

@KerrekSB: Указатели! = Динамическое распределение. Вы можете использовать много указателей, не называя никаких новых или удаленных. – SigTerm

ответ

0

Существует инструмент, который пытается автоматически конвертировать в интеллектуальные указатели. Я никогда не пробовал.Вот цитата из реферата следующей статьи: http://www.cs.rutgers.edu/~santosh.nagarakatte/papers/ironclad-oopsla2013.pdf

Для применения свойств безопасности, которые трудно проверить статический, Броненосец C++ применяет динамические проверки через шаблонные «умные указатели» классы. Используя полуавтоматический инструмент рефакторинга, мы портировали около 50K строк кода на C++ Ironclad

4

Хотя во всем мире просто использовать boost::shared_pointer, вы должны использовать правильный умный указатель в соответствии с семантикой владения.

В большинстве случаев вам необходимо использовать std::unique_ptr по умолчанию, если владение не используется совместно несколькими экземплярами объектов.

Если вы столкнулись с проблемами циклической собственности, вы можете разбить их на boost::weak_ptr.

Также имейте в виду, что при передаче shared_ptr вы всегда должны передавать их по ссылке const по соображениям производительности (избегайте атомного приращения), если вы действительно не хотите передавать права собственности другому объекту.

3

Есть ли подводные камни?

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

Каковы проблемы, которые можно было бы ожидать во время рефакторинга?

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

НЕ слепо заменить все на shared_ptr. Тщательно исследуйте структуру программы и убедитесь, что shread_ptr НЕОБХОДИМО, и он представляет ТОЧНО, что вы хотите.

Кроме того, убедитесь, что вы используете систему управления версиями, который поддерживает простое ветвление (мерзавец или ртутный), поэтому, когда вы ломаете то, что вы можете вернуться к предыдущему состоянию или запустить что-то похожее на «мерзавец делящие», чтобы найти проблему.

очевидно, нужно заменить X * с shared_ptr

Wrong. Это зависит от контекста. Если у вас есть указатель, который указывает на середину некоторого массива (скажем, на манипуляции с пиксельными данными), то вы не сможете заменить его на shared_ptr (и вам не понадобится).Вы должны использовать shared_ptr только тогда, когда вам необходимо обеспечить автоматическое освобождение объекта. Автоматическое освобождение объекта не всегда то, что вы хотите.

+0

«Да, по закону мурфи, если вы вслепую замените каждый указатель на shared_ptr, получится, что вы этого не хотите, и вы проведете следующие 6 месяцев охоты на ошибки, которые вы представили». ... Конечно ... просто заменить не достаточно ... Нам нужно делать больше вещей. Я сказал это в своем вопросе. Но, вероятно, я могу дать вам алгоритм, который может автоматически выполнять рефракционирование и, по-видимому, сохранять семантику. Этот алгоритм может не работать для всех программ. Но алгоритм скажет, когда он потерпел неудачу. Более того, это, вероятно, будет работать для большинства интересных программ. (contd ..) –

+0

Я просил о помощи в разработке этого алгоритма и определении условий на входной программе, чтобы алгоритм доказуемо сохранил семантику. –

+1

@AbhishekAnand: На мой взгляд, рефакторинг подобным образом должен выполняться человеком. Потому что для этого потребуется меньше времени, чем потребуется, чтобы написать программу, чтобы сделать это (и отлаживать/тестировать эту программу). Чтобы выбрать, где использовать shared_ptr, вам нужно понять программу, и автоматизированный инструмент не поймет смысл. – SigTerm

2

Если вы хотите придерживаться повышения, вы должны подумать, хотите ли вы boost :: shared_ptr или boost :: scoped_ptr. Shared_ptr - ресурс, который должен быть разделен между классами, тогда как scoped_ptr больше похож на то, что вам может понадобиться (по крайней мере, в некоторых местах). Scoped_ptr автоматически удалит память, когда она выходит из области видимости.

Будьте осторожны при передаче shared_ptr функции. Общее правило с shared_ptr заключается в передаче по значению, поэтому создается копия. Если вы передадите его по ссылке, счетчик ссылок указателя не будет увеличен. В этом случае вы можете удалить часть памяти, которую вы хотели сохранить.

Однако есть случай, если вы захотите передать shared_ptr по ссылке. То есть, если вы хотите, чтобы память была выделена внутри другой функции. В этом случае просто убедитесь, что вызывающий объект по-прежнему содержит указатель на время жизни вызываемой функции.

void allocPtr(boost::shared_ptr<int>& ptrByRef) 
{ 
    ptrByRef.reset(new int); 
    *ptrByRef = 3; 
} 

int main() 
{ 
    boost::shared_ptr<int>& myPointer; 
    // I want a function to alloc the memory for this pointer. 

    allocPtr(myPointer); // I must be careful that I still hold the pointer 
          // when the function terminates 

    std::cout << *ptrByRef << std::endl; 
} 
2

Я перечисляю шаги/проблемы, связанные с этим. Они работали на меня, но я не могу ручаться, что они на 100% правильны.

0) проверьте, могут ли быть циклические общие указатели. Если это так, может ли это привести к утечке памяти? В моем случае, к счастью, циклы не должны быть разбиты, потому что, если бы у меня был цикл, объекты в цикле были бы полезны и не должны быть уничтожены. использовать слабые указатели для размыкания циклов

1) вам необходимо заменить «most» на shared_ptr<X>. Shared_ptr (только?) Создается сразу после каждого динамического распределения X. Во всяком случае, это копия, построенная или построенная с пустым указателем (для сигнала NULL). Чтобы быть в безопасности (но немного неэффективно), передайте эти shared_ptrs только по ссылке. В любом случае, вероятно, вы никогда не передавали свои указатели со ссылкой, чтобы начать с => никаких дополнительных изменений не требуется

2) Возможно, вы использовали dynamic_cast<X*>(y) в некоторых местах. замените это на dynamic_pointer_cast<X>(y)

3) где бы вы ни проходили NULL (например, чтобы сигнализировать об отказе вычисления), передайте пустой общий указатель.

4) удалить все ВЕЬЕТЕ для соответствующих типов

5) сделать свой базовый класс B наследует от enable_shared_from_this<B>. Затем, где бы вы ни проходили this, перейдите, shared_from_this(). Возможно, вам придется выполнять статическое кастинг, если функция ожидает производного типа. имейте в виду, что, когда вы звоните shared_from_this(), некоторые shared_ptr должны иметь права this. В частности, не вызывайте shared_from_this() в конструкторе класса

Я уверен, что можно полуавтоматизировать этот процесс, чтобы получить семантически эквивалентный, но не обязательно очень эффективный код. Программисту, вероятно, нужно только рассуждать о циклической ссылке (если таковая имеется).

Во многих из этих шагов я многократно использовал regexes. Это заняло около 3-4 часов. Код компилируется и выполнялся правильно до сих пор.

Смежные вопросы