Какие-либо проблемы с этим ссылочным средством доступа константы C++ соединяют интерфейсом с идиомой?

Я преобразовывал структуру в класс, таким образом, я мог осуществить интерфейс метода set для своих переменных.
Я не хотел изменять все экземпляры, где переменная была считана, все же. Таким образом, я преобразовал это:

struct foo_t {
    int x;
    float y;
};

к этому:

class foo_t {
    int _x;
    float _y;
public:
    foot_t() : x(_x), y(_y) {  set(0, 0.0);  }

    const int &x;
    const float &y;

    set(int x, float y)  {  _x = x;  _y = y;  }
};

Я интересуюсь этим, потому что это, кажется, моделирует идею C# общедоступных свойств только для чтения.
Прекрасные компиляции, и я еще не видел проблем.

Помимо шаблона соединения ссылок константы в конструкторе, что оборотные стороны к этому методу?
Какие-либо странные проблемы искажения?
Почему я не видел эту идиому прежде?

6
задан mskfisher 1 June 2010 в 19:35
поделиться

4 ответа

Существует проблема сглаживания, поскольку вы раскрываете ссылку на внутренние данные foo_t, возможно, что код, внешний по отношению к объекту foo_t, будет хранить ссылки на его данные после окончания жизни объекта. Рассмотрим:

foo_t* f = new foo_t();
const int& x2 = f->x;
delete f;
std::cout << x2; // Undefined behavior; x2 refers into a foo_t object that was deleted

Или, еще проще:

const int& x2 = foo_t().x;
std::cout << x2; // Undefined behvior; x2 refers into a foo_t object that no longer exists

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

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

5
ответ дан 8 December 2019 в 12:57
поделиться

Одна проблема в том, что ваш класс больше не может быть скопирован или назначен, и поэтому его нельзя хранить в контейнерах C ++, таких как векторы. Другой - то, что опытные программисты на C ++, обслуживающие ваш код, посмотрят на него и воскликнут «WTF !!» очень громко, что никогда не бывает хорошо.

11
ответ дан 8 December 2019 в 12:57
поделиться

Вы также можете сделать что-то вроде этого, что работает для встроенных типов: (Извините, если этот фрагмент кода содержит ошибки, но вы поняли идею)

template <typename T, typename F>
class read_only{
   typedef read_only<T, F> my_type;
   friend F;

public:
   operator T() const {return mVal;}

private:
   my_type operator=(const T& val) {mVal = val; return *this;}
   T mVal;
};


class MyClass {
public:
   read_only <int, MyClass> mInt;
   void MyFunc() {
      mInt = 7; //Works
   }
};

AnyFunction(){
   MyClass myClass;
   int x = myClass.mVal; // Works (okay it hasnt been initalized yet so you might get a warning =)
   myClass.mVal = 7; // Error
}
2
ответ дан 8 December 2019 в 12:57
поделиться

Ваш подход не является гибким. Когда у вас есть getter / setter для каждой переменной, это означает, что вам не придется переписывать ваш set метод, если вы добавите что-то в ваш класс.

Это не очень хорошо, потому что вы не можете иметь const и non-const геттеры (которые используются редко, но иногда могут быть полезны).

Вы не можете копировать ссылки, поэтому ваш класс становится некопируемым.

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


[Все, что следует далее, полностью субъективно]

Назначение геттеров и сеттеров, на мой взгляд, заключается не в простой модификации, а скорее в инкапсуляции последовательности действий, которые приводят к изменению значения или возвращают значение (которое мы рассматриваем как видимый результат).

Лично struct в случае вашего примера был бы более эффективен, поскольку он оборачивает данные POD и логически "структурирует" их.

0
ответ дан 8 December 2019 в 12:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: