Константное поле ссылки как свойство readonly в классе С++

Это хорошо использовать константное ссылочное поле только для чтения добытчика в классах С++?

Я имею в виду, соответствует ли этот код хорошим практикам?

class check{
private:
    int _x;
public:
    const int& x = _x;
    void setX(int v){
        _x = v;
    }
};

Он очень похож на свойства С#, IMHO и очень простой и чистый код использования класса:

  check s;
  int i;
  std::cin >> i;
  s.setX(i);
  std::cout << s.x << '\n';
  s.setX(7);
  // s.x = i; // Error
  std::cout<<s.x<<'\n';

Ответы

Ответ 1

Соответствует ли этот код хорошей практике?

Не очень, поскольку он вводит ненужную сложность и накладные расходы.

Кроме того, вы не сможете выполнять проверки выполнения и/или утверждения независимо от того, какое значение имеет доступ.

Кроме того, что происходит со временем жизни и семантикой?

Попробуйте назначить один check в своем коде другому и посмотреть, что произойдет. Назначение плохо сформировано, потому что класс не назначается. Вы должны предоставить копию и переместить конструктор, чтобы позаботиться о ссылке, чтобы он не ссылался на старый элемент данных объекта.

Лучше использовать _x напрямую и иметь прямую встроенную функцию getter.


PS: С# -подобные свойства в родном С++?

Ответ 2

Как правило, это не очень хорошая практика.

imho, а также очень простой и понятный код использования класса.

Почему это должно быть яснее и проще?

  • Вы вводите другой элемент переменной (бесполезные служебные данные). (Как правило, ссылка будет реализована как дополнительный указатель элемента).
  • Это делает код сложнее поддерживать. Вы фактически создаете зависимости между переменными членами.
  • Это создает проблемы при назначении и в операциях копирования. Как должна работать копия?

"Классический" подход звучит яснее, например:

class Foo {
 public:
  void set_num(int value) noexcept { m_num = value; }
  int get_num() const noexcept { return m_num; }
  void set_string(std::string value) noexcept {
      m_str = std::move(value);
  }
  const std::string& get_string() const noexcept {
      return m_str;
  }
 private:
  int m_num;
  std::string m_str;
};

С точки зрения характеристик, этот подход должен быть предпочтительным.

  • Сроки: вызов get_variable в встроенной функции не создает дополнительных накладных расходов, чем ваш "ссылочный подход". Более того, он очень оптимизирован компилятором (из-за простого кода).
  • Сложность пространства: он не вводит дополнительный элемент данных.

Ответ 3

То, что вы предлагаете, в общем, плохая идея:

  • Вы не можете реализовать свойство, выполнив какую-либо обработку (например, с помощью геттера вы можете хранить координаты с помощью [x, y], а затем решают изменить реализацию, чтобы использовать [угол, радиус], сохраняя при этом одну и ту же публикацию интерфейс).
  • Использование переменной-члена const связано с нехваткой места и не дает вам каких-либо преимуществ по производительности по сравнению с встроенным getter.
  • Это не идиоматично.
  • Как только вы опубликовали свой класс, и другие люди начнут его использовать, вы застряли с ним: слишком поздно, чтобы изменить его, чтобы использовать метод.

Если ваше намерение со свойствами состоит в том, чтобы сделать код более кратким, вам не нужно использовать слова "get" и "set" в именах ваших функций; это старомодная практика. Вы можете использовать фактическое имя "свойство" в качестве имени функции, и вы можете перегрузить получателя и сеттер:

class Cheque {
public:
    int x() const {
        return x_;
    }
    Cheque & x(int newX) {
        x_ = newX;
        return *this;
    }
private:
    int x_;
}

// Usage:
// int amt = myCheque.x(1234);
// Cheque c = Cheque().x(123);

Возвращая *this, как в приведенном выше коде, вы можете использовать цепочку методов; способ реализации Fluent interface idiom.

Ответ 4

Когда С# компилирует свойство, он скомпилируется в функцию getter и setter.

Вот некоторый код С#, который подтверждает этот факт:

using System;

namespace Reflect
{
    class Program
    {
        class X
        {
            public int Prop { get; set; }
        }

        static void Main(string[] args)
        {
            var type = typeof(X);
            foreach (var method in type.GetMethods())
            {
                Console.WriteLine(method.Name);
            }
            Console.ReadKey();
        }
    }
}

Ваш выход должен быть:

get_Prop
set_Prop
ToString
Equals
GetHashCode
GetType

get_Prop - это функция, реализующая геттер. set_Prop - это функция, которая реализует сеттер.

Итак, даже если то, что вы делаете, похоже, это не то же самое.

Откровенно, почти все, что вы могли бы сделать, чтобы попытаться эмулировать синтаксис свойств в С++, будет так или иначе падать. Большинство решений либо будут стоить вам памяти, либо у него будет некоторое ограничение, что сделает его более громоздким, чем полезным.

Просто учись жить с геттерами и сеттерами. Getters и seters - хорошая практика. Они короткие, они простые, они гибкие, они, как правило, хорошие кандидаты для встраивания, каждый понимает, что они делают и так далее.