web-dev-qa-db-fra.com

Le moyen le plus concis pour désactiver la copie de classe en C++ 11

J'ai un problème avec la génération obsolète C++ 11 de constructeur de copie et d'opérateur d'attribution de copie par défaut lorsqu'il existe un destructeur défini par l'utilisateur.

Pour la plupart des classes suffisamment simples, les constructeurs, les opérateurs et les destructeurs générés par défaut conviennent parfaitement. Considérez les raisons suivantes pour déclarer destructeur:

  1. Rendre virtuel destructeur trivial dans la classe de base:

    // header
    class Base1 { public: virtual ~Base1() = default; };
    class Base2 { public: virtual ~Base2(); };
    // source
    Base2::~Base2() = default;
    

    Les 4 méthodes spéciales de copie et de déplacement seront-elles générées par le compilateur dans ces cas? Si oui, alors je pense que ça va et qu'il n'y a pas besoin de compliquer Base1 ou Base2.

  2. Impression du message de débogage dans le destructeur:

    // header
    class D { public: ~D(); };
    // source
    D::~D() {
    #ifdef DEBUG_THIS
        std::cout << "D was destructed." << std::endl;
    #endif
    }
    

    Je crois que dans ce cas, le constructeur de copie et l'opérateur d'affectation seraient générés; mais déplacer constructeur et opérateur d'affectation ne serait pas. Je veux éviter d'utiliser la génération par défaut obsolète et désactiver la copie de D. Je souhaite également éviter d'inonder D avec 4 déclarations deleted. Désactiver un seul constructeur de copie suffit-il? Est-ce un bon style?

16
vedg
  1. Seuls les constructeurs de copie et les opérateurs d’affectation de copie sont générés lorsque le destructeur est explicitement défini par défaut. Et même dans ce cas, leur génération est déconseillée. Donc, pour avoir un destructeur virtuel et toutes les méthodes par défaut, il faut écrire ce qui suit:

    struct Base
    {
        Base()=default;
        virtual ~Base() = default;
        Base(const Base&)=default;
        Base& operator=(const Base&)=default;
        Base(Base&&)=default;
        Base& operator=(Base&&)=default;
    };
    

    J'utiliserais certainement une macro pour plus d'une classe Base.

  2. Dans le cas où le destructeur est défini par l'utilisateur, 2 méthodes spéciales sont toujours générées. Il existe les manières suivantes de disable deprecated pour générer le constructeur de copie et l'opérateur d'affectation de copie:

    • supprime le constructeur de déplacement OR l'opérateur d'affectation de déplacement (pas tout à fait explicite, mais très court):

      Base(Base&&)=delete; // shorter than deleting assignment operator
      
    • delete both constructeur de copie et opérateur d'affectation de copie:

      Base(const Base&)=delete;
      Base& operator=(const Base&)=delete;
      

    Notez que vous devez explicitement déclarer le constructeur par défaut si vous en avez besoin, par exemple. Base()=default;.

    Une macro ou une classe spéciale héritée peut également être utilisée à cette fin, mais je préfère personnellement supprimer le constructeur de déplacement pour mettre en œuvre ma propre macro ou classe de base. Lorsque vous utilisez Qt ou boost , je préférerais Q_DISABLE_COPY(Base) et hériter de boost::noncopyable respectivement, car ils sont déjà implémentés, largement connus et reconnaissables.

http://accu.org/index.php/journals/1896 - explication détaillée et justification de ces problèmes.

7
vedg

Avec C++ 11, une méthode propre consiste à suivre le modèle utilisé dans Boost (voir ici )

En gros, vous créez une classe de base dans laquelle le constructeur de copie et l'affectation de copie sont supprimés, et vous en héritez:

class non_copyable
{
protected:
    non_copyable() = default;
    ~non_copyable() = default;

    non_copyable(non_copyable const &) = delete;
    void operator=(non_copyable const &x) = delete;
};

class MyClass: public non_copyable
{
...
}
15
quantdev

Supprimer le constructeur de copie et l'opérateur d'affectation de copie est le moyen le plus simple et le plus clair de désactiver la copie:

class X
{
    X(X const &) = delete;
    void operator=(X const &x) = delete;
};

Je ne comprends pas ce dont vous parlez avec les destructeurs virtuels dans le corps de la question. On dirait que vous demandez un moyen de faire en sorte que votre code utilise moins de caractères de code source, mais également plus cryptique pour quiconque le regarde.

Si la liste des fonctions supprimées vous dérange, vous pouvez les cacher derrière une macro, je suppose.

 #define NON_COPYABLE_NOR_MOVABLE(T) \ 
      T(T const &) = delete; \
      void operator=(T const &t) = delete; \
      T(T &&) = delete;
12
M.M

Vous pouvez le faire par ceci (qui est utilisé par Caffe: un cadre rapide pour l’apprentissage en profondeur ):

// Disable the copy and assignment operator for a class.
#define DISABLE_COPY_AND_ASSIGN(classname) \
private:\
  classname(const classname&);\
  classname& operator=(const classname&)

Exemple d'utilisation:

class CNoCopyable{

    public:
        CNoCopyable(int i):m_d(i){}

    private:
        int m_d;
        // add this line(pass class name)
        DISABLE_COPY_AND_ASSIGN(CNoCopyable);

};
1
Jayhello