Осуществление «Правила трех» пошло не так - PullRequest
2 голосов
/ 13 марта 2019

Ниже приведена ошибочная реализация «правила трех», которую я пытаюсь понять.

При отладке программы я обнаружил, что у отладчика есть проблема очистки int *k, которая может быть решеналибо определив int *k = nullptr, либо просто установив для него что-то разумное в конструкторе копирования.

Однако я не понимаю, как возникшая ошибка программы, нарушение прав доступа, возникает.

Я знаю, что после конструктора назначения копирования v1 s int *k больше не указывает на действительный адрес памяти.

class Vector2 {
public:
    std::string name = "default";
    int* k;

    Vector2(int s, std::string n) : k(new int[s]), name(n) {
    }

    Vector2(const Vector2 &other)  {
        std::cout<< "Copy constructor: " << name << std::endl;
    }

    ~Vector2() {
        std::cout << "Deleting: " << name << std::endl;
        delete[] k;
    }

    void swap(Vector2& other) {
        using std::swap;
        swap(k, other.k);
    }

    Vector2& operator=(Vector2 other) {
        std::cout << "Copy assignment constructor: " << name << std::endl;
        swap(other);
        return *this;
    }
};


int main() {
        Vector2 v1 = Vector2(2, "v1");
        Vector2 v2 = Vector2(4, "v2");
        v1 = v2;
        std::cout << &v1 << " " << &v2 << std::endl;
        std::cout << &v1.k << " " << &v2.k << std::endl;
        return 0;
    }

Ниже приведен вывод консоли вышеуказанной программы:

Copy constructor: default
Copy assignment constructor: v1
Deleting: default
0000001B5611FA28 0000001B5611FA78
0000001B5611FA50 0000001B5611FAA0
Deleting: v2
Deleting: v1
16:18:42: The program has unexpectedly finished.

Ответы [ 4 ]

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

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

Для вызова operator=(Vector2 other) вызывается конструктор копирования для создания other (это точкаправила три), поэтому other заполнен дерьмом.Затем вы меняете действительный k из this (он же v1) с дерьмом k из other.

Затем, когда вызывается деструктор v1, он вызывает delete[] k на дерьмовом k -> нарушение доступа.

Решение

Сделайте копию в вашем конструкторе копирования.Или, по крайней мере, правильно инициализируйте k (например, nullptr).

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

Решение можно получить, изложив точную последовательность событий, например: больше распечаток и тестирование, какие параметры вызываются, когда:

Начиная с: v1 = v2;

  1. v2 вызывает копировать конструктор с аргументом other (независимо от того, что есть другое), в частности: его int* k не указывает на допустимую память. Для простоты назовем этот новый Vector2 v3.
  2. Конструктор присваивания теперь вызывается с v3.
  3. Тогда мы начинаем своп.

Ошибка фактически возникает в конструкторе копирования , так как v3 не инициализируется должным образом на шаге 1.

Шаг 2 и шаг 3 в основном «прячутся», перенося ошибку с v3 на v1.

Теперь интересный вопрос, как на самом деле генерируется v3? Не по умолчанию конструктор!

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

Ваш вопрос в Vector2(const Vector2 &other)

Вы используете этот конструктор в вашем operator = неявно, передавая значение; но вы не можете присвоить k любому значению в этом конструкторе.

Это приводит к замене действительного k на недействительный k, а затем удалению недействительного k; в результате чего вы потерпели крах.

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

Конструирование other в operator= использует конструктор копирования, который не создает новую копию указанного значения. Ваш конструктор копирования может даже не копировать k, так как это тип POD и, следовательно, не обязательно созданный по умолчанию или скопированный по умолчанию.

Затем, когда он разрушен, он пытается уничтожить его дважды. Или, в зависимости от случайных факторов, таких как макет стека, он может вообще не копировать k, а затем пытается delete неверный указатель.

...