Поле ссылки Const как свойство readonly в классе C++

хорошо ли использовать поле ссылки const в качестве геттера только для чтения в классах c++?

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

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

он работает очень похоже на свойства C#, 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';

4 ответов


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

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

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

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

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

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


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


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

ИМХО, и очень легкий и чистый в код использования класса.

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

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

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

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 на inline функция не вводит больше накладных расходов, чем ваш "ссылочный подход". Кроме того, весьма оптимизируется компилятором (из-за прямолинейности кода).
  • сложность пространства: он не вводит дополнительный элемент данных.

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

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

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

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 как в приведенном выше коде позволяет использовать способ сцепления; способ реализации свободно интерфейс идиома.


когда в C# компилирует и он компилируется в геттер и сеттер.

вот код C#, который доказывает этот факт:

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 - это функция, реализующая сеттер.

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

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

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