105 votes

C++11 a-t-il des propriétés de type C# ?

En C#, il existe une belle syntaxe pour les champs avec getter et setter. De plus, j'aime les propriétés auto-implémentées qui me permettent d'écrire

public Foo foo { get; private set; }

En C++, je dois écrire

private:
    Foo foo;
public:
    Foo getFoo() { return foo; }

Existe-t-il un concept de ce type dans le C++11 qui me permette de bénéficier d'une certaine souplesse syntaxique à cet égard ?

73 votes

Cela pourrait être fait avec quelques macros. s'enfuit honteusement

0 votes

Je rends tout public et j'accède directement aux champs dans le cadre de l'interface publique de la classe. Si un champ doit vraiment être privé, je le fais précéder d'un trait de soulignement, mais on peut quand même y accéder s'il y a une raison impérative. C'est une mauvaise idée si vous avez des développeurs incompétents dans votre équipe qui en abusent. canards

7 votes

@Eloff : Rendre tout public est TOUJOURS une mauvaise idée.

97voto

psx Points 66

En C++, vous pouvez écrire vos propres fonctionnalités. Voici un exemple d'implémentation de propriétés utilisant des classes non nommées. Article de Wikipedia

struct Foo
{
    class {
        int value;
        public:
            int & operator = (const int &i) { return value = i; }
            operator int () const { return value; }
    } alpha;

    class {
        float value;
        public:
            float & operator = (const float &f) { return value = f; }
            operator float () const { return value; }
    } bravo;
};

Vous pouvez écrire vos propres getters & setters à la place et si vous voulez un accès aux membres de la classe du titulaire, vous pouvez étendre cet exemple de code.

1 votes

Avez-vous des idées sur la façon de modifier ce code pour avoir une variable membre privée à laquelle Foo peut accéder en interne, alors que l'API publique n'expose que la propriété ? Je pourrais bien sûr faire de Foo un ami de alpha/beta, mais je devrais alors toujours écrire alpha.value pour accéder à la valeur, mais je préférerais que l'accès direct à la variable membre depuis l'intérieur de Foo donne l'impression d'accéder à un membre de Foo lui-même et non à un membre d'une classe de propriété imbriquée spéciale.

1 votes

@Kaiserludi Oui : dans ce cas, rendez alpha et bravo privés. Dans Foo, vous pouvez lire/écrire avec ces "propriétés" ci-dessus, mais en dehors de Foo, ce ne serait plus possible. Pour contourner cela, faites une référence const qui est publique. Elle est accessible de l'extérieur, mais seulement en lecture puisqu'il s'agit d'une référence constante. Le seul problème est que vous aurez besoin d'un autre nom pour la référence constante publique. Personnellement, j'utiliserais _alpha pour la variable privée et alpha pour la référence.

1 votes

@Kapichu : pas une solution, pour 2 raisons. 1) En C#, les property getters/setters sont souvent utilisés pour intégrer des contrôles de sécurité qui sont imposés aux utilisateurs publics de la classe tout en permettant aux fonctions membres d'accéder directement à la valeur. 2) Les références constantes ne sont pas libres : en fonction du compilateur/plateforme, elles vont agrandir sizeof(Foo) .

64voto

Michael Litvin Points 524

Le C++ n'intègre pas cette fonctionnalité, mais vous pouvez définir un modèle pour imiter la fonctionnalité des propriétés :

template <typename T>
class Property {
public:
    virtual ~Property() {}  //C++11: use override and =default;
    virtual T& operator= (const T& f) { return value = f; }
    virtual const T& operator() () const { return value; }
    virtual explicit operator const T& () const { return value; }
    virtual T* operator->() { return &value; }
protected:
    T value;
};

A définir une propriété :

Property<float> x;

Pour mettre en œuvre un getter/setter personnalisé juste hériter :

class : public Property<float> {
    virtual float & operator = (const float &f) { /*custom code*/ return value = f; }
    virtual operator float const & () const { /*custom code*/ return value; }
} y;

Pour définir un propriété en lecture seule :

template <typename T>
class ReadOnlyProperty {
public:
    virtual ~ReadOnlyProperty() {}
    virtual operator T const & () const { return value; }
protected:
    T value;
};

Et pour l'utiliser en classe Owner :

class Owner {
public:
    class : public ReadOnlyProperty<float> { friend class Owner; } x;
    Owner() { x.value = 8; }
};

Vous pourriez définir certains des éléments ci-dessus dans macros pour le rendre plus concis.

34voto

CompuChip Points 3381

Il n'y a rien dans le langage C++ qui fonctionne sur toutes les plateformes et tous les compilateurs.

Mais si vous êtes prêt à renoncer à la compatibilité multiplateforme et à vous engager dans un compilateur spécifique, vous pouvez utiliser une telle syntaxe, par exemple dans Microsoft Visual C++ vous pouvez faire

// declspec_property.cpp  
struct S {  
   int i;  
   void putprop(int j) {   
      i = j;  
   }  

   int getprop() {  
      return i;  
   }  

   __declspec(property(get = getprop, put = putprop)) int the_prop;  
};  

int main() {  
   S s;  
   s.the_prop = 5;  
   return s.the_prop;  
}

2 votes

19voto

Niceman Points 1

Avec C++11, vous pouvez définir un modèle de classe de propriété et l'utiliser comme suit :

class Test{
public:
  Property<int, Test> Number{this,&Test::setNumber,&Test::getNumber};

private:
  int itsNumber;

  void setNumber(int theNumber)
    { itsNumber = theNumber; }

  int getNumber() const
    { return itsNumber; }
};

Et voici le modèle de classe de propriété.

template<typename T, typename C>
class Property{
public:
  using SetterType = void (C::*)(T);
  using GetterType = T (C::*)() const;

  Property(C* theObject, SetterType theSetter, GetterType theGetter)
   :itsObject(theObject),
    itsSetter(theSetter),
    itsGetter(theGetter)
    { }

  operator T() const
    { return (itsObject->*itsGetter)(); }

  C& operator = (T theValue) {
    (itsObject->*itsSetter)(theValue);
    return *itsObject;
  }

private:
  C* const itsObject;
  SetterType const itsSetter;
  GetterType const itsGetter;
};

2 votes

Que fait C::* Comment ? Je n'ai jamais rien vu de tel auparavant ?

1 votes

C'est un pointeur vers une fonction membre non statique de la classe C . C'est similaire à un pointeur de fonction ordinaire, mais pour appeler la fonction membre, vous devez fournir un objet sur lequel la fonction est appelée. Ceci est réalisé avec la ligne itsObject->*itsSetter(theValue) dans l'exemple ci-dessus. Voir aquí pour une description plus détaillée de cette fonctionnalité.

0 votes

@Niceman, y a-t-il un exemple d'utilisation ? Il semble que ce soit très coûteux de l'avoir comme membre. Il n'est pas non plus particulièrement utile en tant que membre statique.

19voto

Vous pouvez émuler le getter et le setter jusqu'à un certain point en ayant un membre du type dédié et en surchargeant operator(type) y operator= pour ça. Savoir si c'est une bonne idée est une autre question et je vais +1 Réponse de Kerrek SB pour exprimer mon opinion à ce sujet :)

0 votes

Vous pouvez émuler l'appel d'une méthode lors de l'assignation ou de la lecture par ce type mais vous ne pouvez pas distinguer qui appelle l'opération d'assignation (pour l'interdire si ce n'est pas le propriétaire du champ) - ce que j'essaie de faire en spécifiant un niveau d'accès différent pour le getter et le setter.

0 votes

@Flavius : Il suffit d'ajouter un friend au propriétaire du champ.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X