Есть какие-то проблемы с этой идиомой интерфейса доступа к C ++? - PullRequest
6 голосов
/ 01 июня 2010

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

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 # об открытых свойствах только для чтения.
Компилируется нормально, и я еще не видел никаких проблем.

Помимо того, что шаблон связывает константные ссылки в конструкторе, каковы недостатки этого метода?
Любые странные проблемы с алиасами?
Почему я раньше не видел эту идиому?

Ответы [ 4 ]

11 голосов
/ 01 июня 2010

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

5 голосов
/ 01 июня 2010

Существует проблема псевдонимов, заключающаяся в том, что, поскольку вы предоставляете ссылку на внутренние данные 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 после его жизни, но это может быть сложнее пропустить или сделать случайно.

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

2 голосов
/ 02 июня 2010

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

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
}
0 голосов
/ 01 июня 2010

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

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

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

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


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

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

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

...