О владении объектами кучи и C++ и параметрами передачи по ссылке
Я хотел бы, чтобы мой класс был:
class NumberedString : public Object {
public:
String newName;
short nameID;
NumberedString(String &newName, short nameID) : newName(newName), nameID(nameID) {}
};
HashMap uniqueStrs;//For later.
Инстанциация этого будет передана HashMap
который переходит во владение своим распределением кучи:
В HashMap.h (скажем):
virtual result Add(const Object& key, const Object& value);
Теперь это где я запутался. Я выделял String
в строке, которая называется Add
:
uniqueStrs.Add(*(new String(L"XYX_say")), *pNewLoc);
HashMap
затем освободил бы эту память для меня, несмотря на то, что принял только ссылку на нее. Может быть, я потерял десятилетие C
в течение нового тысячелетия, но я думал, что это невозможно?
Если это не так, я должен быть в состоянии написать что-то вроде:
~NumberedString() {
delete &newName;
}
для моего класса, но я бы никогда не догадался, если бы я не увидел эту библиотеку HashMap::RemoveAll()
делать эквивалент. Этот вопрос утверждает, что это невозможно, но возвращается к опоре на auto_ptr
а также shared_ptr
но моя "платформа поддерживает только STL(Стандартная библиотека шаблонов ( http://www.sgi.com/tech/stl/))". (из всей "Стандартной библиотеки C++"). Могли бы все ответы воздержаться от таких ссылок.
Спасибо.
ССЫЛКИ, подсказанные комментариями
Я не могу опубликовать ссылки как комментарии, поэтому, пожалуйста, смотрите Add
метод и пример его предполагаемого использования: здесь А Benj, String не std::string нет, извините.
ТАКЖЕ
Я знаю, что это может привести к сбоям при попытке удалить объекты стека, но я не понимаю, как HashMap
может претендовать на возможность удаления объектов кучи. Я закодировал вышеупомянутый класс, чтобы попытаться воссоздать это поведение, но я не могу совершить подвиг, поэтому вопрос.
В ответ на "Бесполезный"
@ Бесполезно: не может быть возможно перейти к foo(int &bar)
переменная *pBar
объявил int pBar = new int(1);
а потом foo
предполагает владение
foo(int &bar) {
int *__pBar = &bar;
delete __pBar;
}
? Я собирался попробовать, но я начинаю осторожничать, чтобы не слишком верить тому, что написано в документации. Хотя это было сгенерировано из заголовка, который говорит
class _EXPORT_BASE_ HashMap :
public IMap,
public Object
{
virtual result Add(const Object& key, const Object& value);
//other stuff
};
3 ответа
Ну, в этом нет ничего синтаксически неправильного. Единственное синтаксическое правило для удаления состоит в том, что его операнд должен быть указателем. Семантически: указатель должен быть значением, возвращаемым из new
и вот где эта идиома воняет; если я вижу функцию, принимающую константную ссылку, я обычно оправдываюсь, полагая, что могу передать ей локальную переменную, или временную, или такую. В этом случае delete
собирается вызвать действительно большие проблемы.
В более общем плане, посмотрев на документацию библиотеки: я бы избегал этой библиотеки, как чумы. Это напоминает мне библиотеку от NHS, которая была широко распространена в первые дни C++: она требует, чтобы все происходило из Object
и контейнеры содержат Object*
, Опыт работы с этой библиотекой в то время (конец 1980-х годов) привел к выводу, что она не работает и является частью мотивации для добавления шаблонов в язык, чтобы мы могли писать вещи, которые работали. Используя эту библиотеку, мы возвращаемся на 25 лет назад и отбрасываем все, что мы узнали с тех пор. (Java пошла по аналогичному пути около 10 лет спустя, так что это не что-то особенное для C++. По сути, предлагаемое решение разработано для языков с полной динамической проверкой типов, таких как Lisp, Smalltalk или совсем недавно Python, и не работать на языках со статической проверкой типов, таких как C++ или Java.)
uniqueStrs.Add(*new String(L"XYX_say"), *pNewLoc);
Убраны лишние скобки, которые были неправильными; Я думаю, вы не хотели спрашивать о них.
HashMap тогда освободит эту память для меня, несмотря на то, что я принимаю только ссылку на нее. Может быть, я потерял десятилетие до C за новое тысячелетие, но я думал, что это невозможно?
Можно и delete &newName;
законно, учитывая, что newName
на самом деле является результатом *new ...
, Тем не менее, это unidiomatic особенно с декарацией
virtual result Add(const Object& key, const Object& value);
Поскольку он принимает свои аргументы в качестве const-ссылок, он также может принимать значения, неявно преобразуемые в const-ссылки:
uniqueStrs.Add(String(L"XYX_say"), something)
Это приведет к сбоям (потому что значение перестает существовать после вызова, потому что delete
удалит объект, не относящийся к куче, и т. д.), но интерфейс не показывает его четко, и обычно передается r-значение функциям, принимающим const-ссылки.
Если у вас есть:
class NumberedString : public Object {
String newName;
...
};
компилятор генерирует NumberedString
деструктор, который автоматически вызывает деструкторы всех объектов-членов, в том числе newName
, Поэтому вам не нужно делать что-то подобное (что в любом случае не имеет смысла):
~NumberedString() {
delete &newName;
}
Я посмотрел на Bada API, который для HashMap::Add(const Object& key, const Object& value)
говорит:
Этот метод выполняет поверхностное копирование. Добавляет только указатель; не сам элемент.
Этот интерфейс немного вводит в заблуждение и потенциально опасен - jpalacek объяснил, что может произойти, если вы передадите объект, которого нет в куче. На мой взгляд, эта функция должна иметь типы указателей в качестве аргументов, что было бы намного понятнее.
За HashMap::Remove(const Object& key, bool deallocate = false)
а также HashMap::RemoveAll(bool deallocate = false)
Документация гласит:
Удаляет все указатели объектов в коллекции. Если параметр освобождает значение true, он также удаляет все объекты.
Таким образом, по умолчанию эти функции будут просто удалять указатели, но ваши объекты все еще будут живы.
В вашей текущей реализации NumberedString
несет ответственность за жизнь своих членов. Когда экземпляр этого класса будет уничтожен, его члены будут уничтожены. Если вы передадите его членам HashMap
удалите свой объект / удалите его из стека и затем вызовите HashMap::RemoveAll(false)
, HashMap
не буду пытаться освобождать объекты дважды. Помните, что после удаления вашего объекта HashMap
будет держать указатели на освобожденную память (висячие указатели), что опасно. Если вы позвоните HashMap::RemoveAll(true)
, HashMap
попытается освободить память, которая уже была освобождена, что также опасно.
Лучший дизайн мог бы иметь что-то вроде этого:
class NumberedString : public Object {
String* pNewName;
short* pNameID;
...
}
где вы убедитесь, что NumberedString
не владеет String
а также short
объекты (не удаляет их в своем деструкторе). Этот класс будет держать указатели, так же, как HashMap
, Вам нужно будет определить, кто создает, а кто уничтожает эти объекты. Например, вы можете создать их, передать их адреса NumberedString
а также HashMap
а затем делегировать HashMap
удалить их по телефону HashMap::RemoveAll(true)
, Или вы можете удалить их самостоятельно, а затем позвонить HashMap::RemoveAll(false)
,
Вывод: будьте очень осторожны с этим глупым API и обращайте внимание на то, когда и кем ваши объекты создаются и удаляются.