ti-enxame.com

O C ++ 11 tem propriedades no estilo C #?

Em C #, existe um açúcar de sintaxe agradável para campos com getter e setter. Além disso, gosto das propriedades auto-implementadas que me permitem escrever

public Foo foo { get; private set; }

Em C++ eu tenho que escrever

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

Existe algum conceito no C++ 11 que me permita ter um pouco de açúcar de sintaxe nisso?

85
Radim Vansa

Em C++, você pode escrever seus próprios recursos. Aqui está um exemplo de implementação de propriedades usando classes sem nome. artigo da 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;
};

Você pode escrever seus próprios getters e setters no local e, se desejar acesso de membro da classe titular, poderá estender esse código de exemplo.

74
psx

O C++ não possui isso incorporado, você pode definir um modelo para imitar a funcionalidade das propriedades:

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

Para definir uma propriedade:

Property<float> x;

Para implementar um getter/setter personalizado basta herdar:

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;

Para definir um propriedade somente leitura:

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

E para se-o na classe Owner:

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

Você pode definir alguns dos itens acima em macros para torná-lo mais conciso.

40
Michael Litvin

Não há nada na linguagem C++ que funcione em todas as plataformas e compiladores.

Mas se você estiver disposto a quebrar a compatibilidade entre plataformas e se comprometer com um compilador específico, poderá usar essa sintaxe, por exemplo: no Microsoft Visual C++ você pode fazer

// 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;  
}
20
CompuChip

Você pode emular getter e setter até certo ponto, tendo um membro do tipo dedicado e substituindo operator(type) e operator= para ele. Se é uma boa ideia, é outra pergunta e eu vou à resposta de +1 Kerrek SB para expressar minha opinião :)

18
Michael Krelin - hacker

Talvez dê uma olhada na classe de propriedade que montei durante as últimas horas: https://codereview.stackexchange.com/questions/7786/c11-feedback-on-my-approach-to-c-like- propriedades da classe

Ele permite que você tenha propriedades que se comportam assim:

CTestClass myClass = CTestClass();

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

Com o C++ 11, você pode definir um modelo de classe Property e usá-lo assim:

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

E aqui está o modelo 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;
};
16
Niceman

Como muitos outros já disseram, não há suporte embutido no idioma. No entanto, se você estiver direcionando o compilador Microsoft C++, poderá tirar proveito da extensão específica da Microsoft para propriedades documentadas aqui.

Este é o exemplo da página vinculada:

// 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
Trasplazio Garzuglio

Não, o C++ não tem conceito de propriedades. Embora possa ser estranho definir e chamar getThis () ou setThat (valor), você está declarando ao consumidor desses métodos que alguma funcionalidade pode ocorrer. O acesso aos campos em C++, por outro lado, informa ao consumidor que nenhuma funcionalidade adicional ou inesperada ocorrerá. As propriedades tornariam isso menos óbvio, pois o acesso à propriedade, à primeira vista, parece reagir como um campo, mas de fato reage como um método.

Além disso, eu estava trabalhando em um aplicativo .NET (um CMS muito conhecido) tentando criar um sistema de associação ao cliente. Devido à maneira como eles usaram propriedades para seus objetos de usuário, foram disparadas ações que eu não havia previsto, fazendo com que minhas implementações fossem executadas de maneiras bizarras, incluindo recursão infinita. Isso ocorreu porque seus objetos de usuário fizeram chamadas para a camada de acesso a dados ou a algum sistema de armazenamento em cache global ao tentar acessar coisas simples como StreetAddress. Todo o seu sistema foi baseado no que eu chamaria de abuso de propriedades. Se eles tivessem usado métodos em vez de propriedades, acho que teria descoberto o que estava acontecendo de errado muito mais rapidamente. Se eles tivessem usado campos (ou pelo menos tenham feito suas propriedades se comportarem mais como campos), acho que o sistema teria sido mais fácil de estender e manter.

[Edit] Mudou meus pensamentos. Eu tive um dia ruim e fiquei um pouco em um discurso retórico. Essa limpeza deve ser mais profissional.

12
David Peterson

Baseado em https://stackoverflow.com/a/23109533/404734 aqui está uma versão com um getter público e um setter privado:

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

Você provavelmente sabe disso, mas eu simplesmente faria o seguinte:

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

Essa abordagem é simples, não usa truques inteligentes e faz o trabalho!

Porém, o problema é que algumas pessoas não gostam de prefixar seus campos particulares com um sublinhado e, portanto, não podem realmente usar essa abordagem, mas, felizmente, para quem o usa, é realmente simples.:)

Os prefixos get e set não agregam clareza à sua API, mas os tornam mais detalhados, e a razão pela qual não acho que eles adicionem informações úteis é porque quando alguém precisa usar uma API, se a API faz sentido, ela provavelmente perceberá o que é isso. faz sem os prefixos.

Mais uma coisa, é fácil entender que essas são propriedades porque name não é um verbo.

Na pior das hipóteses, se as APIs forem consistentes e a pessoa não perceber que name() é um acessador e name(value) é um mutador, ela precisará procurar uma vez na documentação para entender o padrão.

Por mais que eu goste de C #, acho que o C++ não precisa de propriedades!

5
Eyal Solnik

Isso não é exatamente uma propriedade, mas faz o que você deseja da maneira mais simples:

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

Aqui, o grande X se comporta como public int X { get; private set; } na sintaxe C #. Se você deseja propriedades completas, fiz uma primeira tentativa para implementá-las aqui .

4
Jan Turoň

Não. Mas você deve considerar se é apenas a função get: set e nenhuma tarefa adicional pré-executada nos métodos get: set apenas a torna pública.

4
Daniel Mor

Eu coletei as idéias de várias fontes C++ e coloquei em um bom exemplo ainda simples para getters/setters em 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;
}

Saída:

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

Você pode testá-lo online aqui: http://codepad.org/zosxqjTX

1
lama12345

Você realmente precisa impor algumas invariáveis ​​ou é apenas um agrupamento lógico de elementos membros? Se for o último, você deve considerar tornar a coisa uma estrutura e acessar os membros diretamente.

1
emsr