J'ai appris que lorsque vous utilisez des pointeurs dans une classe, vous devez implémenter la règle de 5. Si vous n'utilisez pas de pointeurs, vous pouvez, et en fait, il est préférable d'utiliser les valeurs par défaut. Cependant, comment cela fonctionne-t-il avec des pointeurs intelligents? Par exemple, une classe contenant un int* pourrait ressembler à ceci:

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;
    };


};

Mais si nous utilisons des pointeurs intelligents, est-il suffisant de le faire?

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

public:

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

};
1
CiaranWelsh 15 avril 2020 à 10:57

3 réponses

Meilleure réponse

Oui, cela suffit. Le pointeur unique gère la mémoire. Cependant, vos deux classes se comporteront différemment car std::unique_ptr ne peut pas être copié, il n'y aura donc pas de constructeur de copie généré par le compilateur ni d'affectation pour B.

Notez également que vous avez implémenté toutes les méthodes pour la règle de 5, mais pas correctement. Comme mentionné dans un commentaire, la copie d'un A entraînera deux instances ayant le même pointeur et le supprimera lors de la destruction. En fait, obtenir ce droit est tout le point sur la règle de 3/5 et pourquoi vous devriez préférer la règle de 0.

3
idclev 463035818 15 avril 2020 à 08:13

Ceux-ci ont un comportement différent. A peut être copié, B ne peut être déplacé.

N.B. votre implémentation de A n'est pas sûre, cela peut entraîner des fuites et un comportement indéfini.

Une comparaison à l'identique serait soit la copie de 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)) {};

};

Ou définir la copie de 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
Caleth 15 avril 2020 à 08:16

Si vous utilisez un pointeur intelligent (ou l'un des conteneurs std ::), le destructeur de classe par défaut appellera le destructeur du pointeur intelligent (et des conteneurs). Plus d'informations sur ce sujet ici: Pourquoi le destructeur par défaut C ++ détruire mes objets?

1
Jean-Marc Volle 15 avril 2020 à 08:04