Почему после удаления объекта, ссылка на который продолжает жить? - PullRequest
0 голосов
/ 29 октября 2018

У меня есть такая программа:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

class no_object : public std::exception
{
    protected:
        std::string mMsg;
    public:
        no_object(const char* msg) : mMsg(msg) {}
        virtual ~no_object() noexcept override {}
        virtual const char* what() const noexcept override { return mMsg.c_str(); }
};

using namespace std;

class Object {
public:
    Object(const string info) : information(info) { cout << "Object constructor!" << endl; }
    ~Object() { cout << "Object destructor!" << endl; }
    Object(const Object& obj) { information = obj.information; cout << "Copy constructor!" << endl; }
    void setInformation() {  }
    string getInformation() const { return information; }
private:
    string information;
};

class Storage {
public:
    Storage(const size_t width) {
        objs = static_cast<Object*>(malloc(sizeof(Object) * width));

        if (objs == NULL)
            throw std::bad_alloc();

        lastPointer = objs + sizeof (Object) * (width - 1);
    }

    void storeObject(Object& obj, size_t index) {
        if (isIndexOutOfRange(index))
            throw std::out_of_range("Oops, index is out of range!");

        availableIndexes.push_back(index);
        objs[index] = obj;
    }

    Object& getObjectAtIndex(size_t index) const {
        if (isIndexOutOfRange(index))
            throw std::out_of_range("Oops, index is out of range!");

        auto it = find(availableIndexes.begin(), availableIndexes.end(), index);
        if (it == availableIndexes.end())
            throw no_object("Oops, the object for this index is not set!");

        return objs[index];
    }

    ~Storage() {
        free(objs);
    }
private:
    bool isIndexOutOfRange(size_t index) const noexcept {
        Object* indexPointer = objs + sizeof (Object) * index;

        if (indexPointer > lastPointer)
            return true;

        return false;
    }

    vector<size_t> availableIndexes;

    Object* objs;
    Object* lastPointer;
};

int main()
{
    Storage storage(3);
    {
        cout << "1" << endl;
        Object obj = Object("lambo");
        cout << "2" << endl;
        Object& objRef = obj;
        cout << "3" << endl;
        storage.storeObject(objRef, 2);
    }

    cout << "4" << endl;
    Object savedObject = storage.getObjectAtIndex(2);
    cout << "Information from stored object is " << savedObject.getInformation() << endl;

    return 0;
}

Интересно, что у меня есть следующий вывод:

1
Object constructor!
2
3
Object destructor!
4
Copy constructor!
Information from stored object is lambo
Object destructor!

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

Вот мои вопросы:
1. Могу ли я использовать ссылку таким образом. Это безопасно?
2. Почему конструктор копирования называется?
3. Существуют ли другие проблемы в моем коде?
4. Как исправить эту программу, чтобы она была правильной?

Заранее спасибо.

1 Ответ

0 голосов
/ 29 октября 2018

Эта программа хранит ссылки на объекты, а затем мы можем получить их

Не хранит ссылки. Хранит объекты.

  1. Могу ли я использовать ссылку таким образом. Это безопасно?

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

  1. Почему вызывается конструктор копирования?

Поскольку Object savedObject = storage.getObjectAtIndex(2); является инициализацией копирования.

  1. Существуют ли другие проблемы в моем коде?

Вы выделяете блок памяти, используя malloc, не создавая в памяти ни одного экземпляра Object. Затем в строке objs[index] = obj; вы копируете и назначаете экземпляр Object, который находится в памяти ... за исключением того, что нет экземпляра Object, поскольку вы никогда не создавали его. Как следствие, поведение вашей программы не определено.

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

Чтобы устранить обе проблемы, используйте std::vector вместо попытки ручного управления памятью.

Кроме того, lastPointer = objs + sizeof (Object) * (width - 1); находится далеко за пределами выделенной памяти. Возможно, вы упустили из виду тот факт, что арифметика указателей работает с шагом в размере объекта. Дальнейшее умножение на sizeof (Object) не имеет смысла в этом контексте.

...