О владении объектами кучи и C ++ и параметрами передачи по ссылке - PullRequest
0 голосов
/ 09 февраля 2012

Я хотел бы, чтобы мой класс был:

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 no, извините.

ТАКЖЕ

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

В ответ на «Бесполезно»

@ Useless: Mightnнельзя ли передать 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 ]

5 голосов
/ 09 февраля 2012

Ну, в этом нет ничего синтаксически неправильного. Единственный Синтаксическое правило для удаления состоит в том, что его операнд должен быть указателем. Семантически: указатель должен быть значением, возвращаемым из new, и это где эта идиома воняет; если я вижу функцию, принимающую константную ссылку, Я обычно оправдываюсь в предположении, что я могу передать ему локальную переменную, или временный, или такой. В этом случае, delete собирается вызвать действительно большие проблемы.

В общем, посмотрев на документацию библиотеки: я бы избегайте этой библиотеки, как чумы. Это напоминает мне библиотеку из NHS, которая была широко распространена в первые дни C ++: она требует все происходит от Object, а контейнеры содержат Object*. Опыт работы с этой библиотекой в ​​то время (конец 1980-х) вывод, что это не сработало, и были частью мотивации добавление шаблонов к языку, чтобы мы могли писать вещи, которые делали Работа. Использование этой библиотеки в основном уходит в прошлое на 25 лет, и выбрасывая все, что мы узнали с тех пор. (Java следовал за похожий маршрут около 10 лет спустя, так что это не что-то конкретное для C ++. По сути, предлагаемое решение разработано для языки с полной динамической проверкой типов, такие как Lisp, Smalltalk или более недавно Python, и не работает в языках со статическим типом проверка, как в C ++ или Java.)

1 голос
/ 09 февраля 2012
uniqueStrs.Add(*new String(L"XYX_say"), *pNewLoc);

Убраны лишние скобки, которые были неправильными;Я думаю, вы не хотели спрашивать о них.

HashMap тогда освободил бы эту память для меня, несмотря на то, что только принял ссылку на нее.Может быть, я потерял десятилетие для C за новое тысячелетие, но я подумал, что это невозможно?

Возможно, и delete &newName; законно, , учитывая, что newNameна самом деле результат *new ....Тем не менее, он неидиоматичен, особенно с декарацией

virtual result Add(const Object& key, const Object& value);

Поскольку он принимает свои аргументы в качестве const-ссылок, он также может принимать значения, неявно преобразуемые в const ссылки:

uniqueStrs.Add(String(L"XYX_say"), something)

Это будетпривести к сбоям (потому что rvalue перестает существовать после вызова, потому что delete удалит объект, не относящийся к куче и т. д.), но интерфейс явно не показывает его, и обычно передают rvalue функциям, принимающим const-references.

0 голосов
/ 09 февраля 2012

Если у вас есть:

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) документация гласит:

Удаляет все указатели объектовв коллекции.Если параметр deallocate имеет значение 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 и обращайте внимание на то, когда и кем ваши объекты создаются и удаляются.

...