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

Другие вопросы по тегам