web-dev-qa-db-fra.com

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

En C #, il existe un sucre de syntaxe de Nice 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; }

Y at-il un tel concept dans le C++ 11 me permettant d'avoir un sucre de syntaxe à ce sujet?

87
Radim Vansa

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 du wiki

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 et setters. Si vous souhaitez un accès membre titulaire, vous pouvez étendre cet exemple de code.

77
psx

C++ n’a pas cette fonction intégrée, 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;
};

Pour définir une propriété:

Property<float> x;

Pour implémenter un getter/setter personnalisé, héritez simplement:

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 à tilisez-le dans la classe Owner:

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

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

45
Michael Litvin

Rien dans le langage C++ ne fonctionne sur toutes les plateformes et les compilateurs.

Mais si vous êtes prêt à rompre la compatibilité entre plates-formes et à vous engager dans un compilateur spécifique, vous pourrez peut-être utiliser cette syntaxe, par exemple dans Microsoft Visual C++ vous pouvez le 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;  
}
21
CompuChip

Vous pouvez émuler getter et setter dans une certaine mesure en ayant un membre de type dédié et en remplaçant operator(type) et operator= pour ça. Que ce soit une bonne idée est une autre question et je vais au +1 La réponse de Kerrek SB à exprimer mon opinion à ce sujet :)

18

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

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 Property.

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

Jetez peut-être un coup d’œil à la classe de propriétés que j’ai assemblée au cours des dernières heures: https://codereview.stackexchange.com/questions/7786/c11-feedback-on-my-approach-to-c-like- propriétés de classe

Cela vous permet d'avoir des propriétés se comportant comme ceci:

CTestClass myClass = CTestClass();

myClass.AspectRatio = 1.4;
myClass.Left = 20;
myClass.Right = 80;
myClass.AspectRatio = myClass.AspectRatio * (myClass.Right - myClass.Left);
17
thesaint

Comme beaucoup d'autres l'ont déjà dit, il n'y a pas de support intégré dans la langue. Cependant, si vous ciblez le compilateur Microsoft C++, vous pouvez tirer parti de l'extension spécifique à Microsoft pour les propriétés documentées ici.

Voici l'exemple de la page liée:

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

Non, C++ n'a pas de concept de propriétés. Bien qu'il soit difficile de définir et d'appeler getThis () ou setThat (valeur), vous indiquez au consommateur de ces méthodes que certaines fonctionnalités peuvent apparaître. D'autre part, l'accès aux champs en C++ indique au consommateur qu'aucune fonctionnalité supplémentaire ou inattendue ne se produira. Les propriétés rendraient cela moins évident car l'accès de propriété semble au premier abord réagir comme un champ, mais en fait, il réagit comme une méthode.

En passant, je travaillais dans une application .NET (un CMS très connu) qui tentait de créer un système d'adhésion client. En raison de la manière dont ils utilisaient les propriétés pour leurs objets utilisateur, des actions que je n'avais pas prévues se déclenchaient, ce qui entraînait l'exécution de mes implémentations de manière étrange, y compris une récursion infinie. En effet, leurs objets utilisateur ont appelé la couche d'accès aux données ou un système de mise en cache global lors de tentatives d'accès à des éléments simples tels que StreetAddress. Tout leur système a été fondé sur ce que j'appellerais un abus de propriété. S'ils avaient utilisé des méthodes au lieu de propriétés, je pense que j'aurais compris beaucoup plus rapidement ce qui n'allait pas. S'ils avaient utilisé des champs (ou du moins fait en sorte que leurs propriétés se comportent davantage comme des champs), je pense que le système aurait été plus facile à étendre et à maintenir.

[Modifier] changé mes pensées. J'avais passé une mauvaise journée et je me suis un peu déchaîné. Ce nettoyage devrait être plus professionnel.

12
David Peterson

Sur la base de https://stackoverflow.com/a/23109533/404734 voici une version avec un getter public et un setter privé:

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

Vous le savez probablement, mais je ferais simplement ce qui suit:

class Person {
public:
    std::string name() {
        return _name;
    }
    void name(std::string value) {
        _name = value;
    }
private:
    std::string _name;
};

Cette approche est simple, ne nécessite aucune astuce et fait le travail!

Le problème est que certaines personnes n'aiment pas préfixer leurs champs privés d'un trait de soulignement et ne peuvent donc pas utiliser cette approche, mais heureusement pour ceux qui le font, c'est vraiment simple.:)

Les préfixes get et set n'apportent pas de clarté à votre API, mais les expliquent davantage. La raison pour laquelle je ne pense pas qu'ils ajoutent des informations utiles est que, lorsque quelqu'un doit utiliser une API si l'API est logique, elle réalisera probablement de quoi elle dispose. se passe des préfixes.

Une dernière chose, il est facile de comprendre que ce sont des propriétés, car name n’est pas un verbe.

Dans le pire des cas, si les API sont cohérentes et si la personne ne se rend pas compte que name() est un accesseur et que name(value) est un mutateur, elle n'aura qu'à la rechercher une fois dans le documentation pour comprendre le motif.

Autant que j'aime le C #, je ne pense pas que le C++ ait besoin de propriétés!

5
Eyal Solnik

Ce n'est pas exactement une propriété, mais il fait ce que vous voulez de la manière simple:

class Foo {
  int x;
public:
  const int& X;
  Foo() : X(x) {
    ...
  }
};

Ici le grand X se comporte comme public int X { get; private set; } en syntaxe C #. Si vous voulez des propriétés complètes, j'ai fait un premier essai pour les implémenter ici .

4
Jan Turoň

Nope .. Mais vous devriez vous demander si c’est juste get: définir la fonction et aucune tâche supplémentaire préformée dans les méthodes get: set, il suffit de la rendre publique.

4
Daniel Mor

J'ai rassemblé les idées de plusieurs sources C++ et les ai placées dans un bel exemple simple et assez simple pour les accesseurs/régleurs en C++:

class Canvas { public:
    void resize() {
        cout << "resize to " << width << " " << height << endl;
    }

    Canvas(int w, int h) : width(*this), height(*this) {
        cout << "new canvas " << w << " " << h << endl;
        width.value = w;
        height.value = h;
    }

    class Width { public:
        Canvas& canvas;
        int value;
        Width(Canvas& canvas): canvas(canvas) {}
        int & operator = (const int &i) {
            value = i;
            canvas.resize();
            return value;
        }
        operator int () const {
            return value;
        }
    } width;

    class Height { public:
        Canvas& canvas;
        int value;
        Height(Canvas& canvas): canvas(canvas) {}
        int & operator = (const int &i) {
            value = i;
            canvas.resize();
            return value;
        }
        operator int () const {
            return value;
        }
    } height;
};

int main() {
    Canvas canvas(256, 256);
    canvas.width = 128;
    canvas.height = 64;
}

Sortie:

new canvas 256 256
resize to 128 256
resize to 128 64

Vous pouvez le tester en ligne ici: http://codepad.org/zosxqjTX

1
lama12345

Votre classe a-t-elle vraiment besoin d'appliquer un invariant ou s'agit-il simplement d'un regroupement logique d'éléments membres? Si c'est le dernier cas, vous devriez envisager de transformer la chose en structure et d'accéder directement aux membres.

1
emsr