Как я могу выделить память в стеке для указателя на объект? - PullRequest
0 голосов
/ 13 апреля 2020

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

Я пытался изменить first = new Node; на first = &Node(); внутри метода push(), но затем я получаю ошибку во время выполнения std::bad_alloc при вызове метода pop(). Я предполагаю, что этот Node создается в стеке и уничтожается, как только программа выходит из метода push(). Итак, мой вопрос: есть ли способ, которым я могу создать присвоение first a Node, созданное в стеке, и сохранить его для дальнейшего использования?

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

class StackOfStrings
{
public:
    StackOfStrings() {}; // Could be omitted, default constructor

    void push(std::string item)
    {
        Node* old_first = first;
        first = new Node;         // *** my question revolves around this line ***
                                  // ***    "first = &Node()" does not work    ***
                                  // ***  how can I have a Node on the stack?  ***
        first->setItem(item);
        first->setNext(old_first);
    }

    std::string pop()
    {
        std::string item = first->getItem();
        first = first->getNext();
        return item;
    }

private:
    Node *first = nullptr;
};

Для полноты, это класс Node:

class Node
{
public:
    std::string getItem() { return item; }
    Node* getNext() { return next; }
    void setItem(std::string item) { this->item = item; }
    void setNext(Node *node) { this->next = node; }

private:
    std::string item;
    Node* next = nullptr;
};

edit: по ошибке я написал * Node вместо этого из & узла. Я обновил вопрос.

edit 2: небольшой эксперимент, который я попытался:

  • Я вызвал push() метод StackOfStrings, чтобы вставить слово "привет" в связанный list.
  • вставил точку останова в метод setItem() из Node, чтобы проверить, что мир "привет" был сохранен в памяти. Действительно, по адресу памяти 0x00FFF900 я нашел значения 68 65 6c 6c 6f, что соответствует "привет" в ASCII
  • Затем я поместил другую точку останова в вызове к getItem(), чтобы проанализировать тот же адрес памяти снова. То, что я нашел сейчас a0 fb ff 00 28, это мусор и, конечно, не "привет".

Ответы [ 2 ]

2 голосов
/ 13 апреля 2020

Во-первых, это очень и очень плохая идея.

Во-вторых, вы не должны брать адрес временного объекта, поскольку он будет уничтожен после того, как точка с запятой будет достигнута, а указатель указывает на недопустимый объект. Я думаю, что эта строка даже не должна компилироваться! Используете ли вы какое-либо расширение Visual C ++?

first = &Node();

вы создаете временный элемент в стеке, вызывая Node(), затем привязываете его адрес к указателю first, затем временный уничтожается.

В-третьих, вы получаете исключение из-за этого:

ваш getItem() метод возвращается по копии, а не по ссылке, поэтому однажды вызванный в pop() по висячему указателю first конструктор std::string вызывается для копирования члена std::string item, который недопустим, поскольку был уничтожен с самого начала.

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

Это число очень велико, и Windows не может выделить для него память в куче, поэтому создается исключение типа std::bad_alloc

Если число было достаточно маленьким, но не меньше std::string массива внутреннего стека, вы можете получить нарушение прав доступа, потому что конструктор копирования попытается скопировать бафф из-за неправильного адреса памяти. Или еще хуже! Вы можете испортить кучу или прочитать несколько случайных байтов, если этот кусок был связан с любым другим объектом в программе

1 голос
/ 13 апреля 2020

Как выделить в стеке память для указателя на объект?

Путем создания автоматической переменной c. Например:

void foo() {
    Node* ptr; // this pointer is allocated on the execution stack

есть ли способ, которым я могу создать назначение для первого узла, созданного в стеке

Конечно. Создайте автомат c Node и используйте оператор addressof:

Node node;
StackOfStrings sos;
sos.first = &node;

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

и сохранить его для дальнейшего использования?

Automati c переменные (т.е. все переменные, расположенные в стеке выполнения) уничтожаются в конце их области видимости. Без исключений. Если позднее использование в этой области, то нет проблем. Если последующее использование выходит за пределы этой области, то желание использовать стек выполнения является ошибкой.

...