Как умные указатели влияют на правило 5? - PullRequest
1 голос
/ 15 апреля 2020

Я узнал, что когда вы используете указатели в классе, вы должны реализовать правило 5. Если вы не используете указатели, то все в порядке, и на самом деле это предпочтительнее, использовать значения по умолчанию. Однако как это работает с умными указателями? Например, класс, содержащий int*, может выглядеть так:

class A {
private:
    int *num_;
public:

    explicit A(int* num) : num_(num) {}

    ~A() {
        delete num_;
    }

    A(const A &other) {
        if (this != &other) {
            num_ = other.num_;
        }
    }

    A(A &&other) noexcept {
        if (this != &other) {
            num_ = other.num_;
        }
    }

    A &operator=(A &other) {
        if (this == &other) {
            this->num_ = other.num_;
        }
        return *this;
    }

    A &operator=(A &&other) noexcept {
        if (this == &other) {
            this->num_ = other.num_;
        }
        return *this;
    };


};

Но если мы используем умные указатели, достаточно ли просто сделать это?

class B {
private:
    std::unique_ptr<int> num_;

public:

    explicit B(int num) : num_(std::make_unique<int>(num)) {};

};

Ответы [ 3 ]

3 голосов
/ 15 апреля 2020

Да, этого достаточно. Уникальный указатель действительно управляет памятью. Однако два ваших класса будут вести себя по-разному, потому что std::unique_ptr не может быть скопировано, следовательно, не будет сгенерированного компилятором конструктора копирования или назначения для B.

Также обратите внимание, что вы реализовали все Методы по правилу 5, но не правильно. Как упомянуто в комментарии, копирование A приведет к тому, что два экземпляра будут иметь один и тот же указатель и будут удалены при уничтожении. На самом деле, правильно понять это - главное в правиле 3/5 и почему вы должны предпочесть правило 0.

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

У них другое поведение. A можно скопировать, B можно только переместить.

Обратите внимание, что ваша реализация A небезопасна, это может привести к утечкам и неопределенному поведению.

Сравнение сопоставимых элементов будет либо копией delete A.

class A {
private:
    int *num_;
public:

    explicit A(int num) : num_(new int(num)) {}

    ~A() {
        delete num_;
    }

    A(const A &other) = delete;

    A(A &&other) noexcept 
     : num_(std::exchange(other.num, nullptr)) {}

    A &operator=(const A &other) =delete;

    A &operator=(A &&other) noexcept {
        swap(num_, other.num_);
        return *this;
    };
};

class B {
private:
    std::unique_ptr<int> num_;

public:

    explicit B(int num) : num_(std::make_unique<int>(num)) {};

};

Или определить B копию

class A {
private:
    int *num_;
public:

    explicit A(int num) : num_(new int(num)) {}

    ~A() {
        delete num_;
    }

    A(const A &other) 
     : A(other.num) {}

    A(A &&other) noexcept 
     : num_(std::exchange(other.num, nullptr)) {}

    A &operator=(const A &other) {
        *num_ = *other.num;
        return *this;
    }

    A &operator=(A &&other) noexcept {
        swap(num_, other.num_);
        return *this;
    };
};

class B {
private:
    std::unique_ptr<int> num_;

public:

    explicit B(int num) : num_(std::make_unique<int>(num)) {};
    ~B() = default;
    B(const B & other) : B(*other.num_) {}
    B(B && other) = default;
    B& operator=(const B & other) { *num_ = *other.num_ }
    B& operator=(B && other) = default;

};
1 голос
/ 15 апреля 2020

Если вы используете умный указатель (или любой из std :: container), деструктор класса по умолчанию вызовет деструктор умного указателя (и контейнеров). Подробнее об этом topi c здесь: Почему деструктор C ++ по умолчанию не уничтожает мои объекты?

...