Почему следующий класс не выполняет поверхностное копирование массива arr? - PullRequest
0 голосов
/ 12 марта 2019

Я реализую следующий класс для стека, основанного на массивах фиксированного размера:

class Stack{
    private:
        int arr[1000];
        int size;
    public:
        Stack(){ size=0; }
        int top(){
            assert(size>0);
            return arr[0];
        }
        void push(int v){ assert(size<1000); arr[size++] = v; }
        void pop(){ assert(size>0); size--; }
        bool empty(){ return size==0; }
        bool is_equal(Stack s){
            Stack c = *this;
            while(!c.empty() && !s.empty()){
                if (c.top() != s.top())
                    return false;
                c.pop();
                s.pop();
            }
            return c.empty() && s.empty();
        }
};

Насколько мне известно, в первой строке метода is_equal будет создан другой объект (c), но атрибут arrбудет указывать на те же данные исходного объекта.Таким образом, извлечение данных из c повлияет на исходную сумму стека.

Удивительно, но этого не происходит.

Адрес arr стека c отличается от адреса arr исходного стека иданные копируются правильно, как будто я перегружен оператором присваивания.

Я что-то упустил?

Ответы [ 2 ]

2 голосов
/ 12 марта 2019

Насколько мне известно, первая строка метода is_equal создаст другой объект (c), но атрибут arr будет указывать на те же данные исходного объекта.Таким образом, извлечение данных из c повлияет на исходную сумму стека.

Нет, это неправильно.Объект Stack будет скопирован и получит свое собственное хранилище.Копирование массива означает копирование всех его элементов в другую часть памяти.c и *this - это два разных объекта Stack с двумя разными массивами.

0 голосов
/ 13 марта 2019

Насколько мне известно, в первой строке метода is_equal будет создан другой объект (c), но атрибут arr будет указывать на те же данные исходного объекта.

Нет, не будет. Нет такой вещи, как создание мелкой копии фиксированного массива. Элементы массива в this будут скопированы в массив в новом объекте c. Так же, как элементы любого объекта Stack, который вы передаете в is_equal(), будут скопированы в массив в параметре s, поскольку вы передаете его по значению .

Если вы действительно хотите поделиться массивом, вам необходимо динамически выделить массив с помощью new[] или std::malloc(), а затем Stack сохранить указатель на массив. Копирование указателя с одного объекта на другой даст вам семантику мелкой копии, о которой вы думаете.

Таким образом, извлечение данных из c повлияет на исходную сумму стека.

Нет, не будет, потому что c имеет свой собственный массив, который не зависит от массива в s или this.

Адрес arr стека c отличается от адреса arr исходного стека

Правильно. Потому что они являются отдельными массивами в памяти.

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

Да, потому что вы не реализовали свой собственный конструктор копирования или оператор присваивания копии, поэтому компилятор неявно реализовал их для вас. Неявная реализация скопирует элементы массива из массива исходного объекта в массив целевого объекта.

И кстати, ваш is_equal() может быть реализован без необходимости делать какие-либо копии:

class Stack {
    private:
        int arr[1000];
        int size;
    public:
        ...
        bool is_equal(const Stack &s) const {
            if (size != s.size)
                return false;
            for (int i = 0; i < size; ++i) {
                if (arr[i] != s.arr[i])
                    return false;
            }
            return true;
        }
        ...
};

Или просто:

#include <algorithm>

class Stack {
    private:
        int arr[1000];
        int size;
    public:
        ...
        bool is_equal(const Stack &s) const {
            // pre-C++14...
            return (size == s.size) && std::equal(arr, arr + size, s.arr);

            // C++14 and later...
            return std::equal(arr, arr + size, s.arr, s.arr + s.size);
        }
        ...
};
...