конст и указатели - PullRequest
       12

конст и указатели

3 голосов
/ 27 января 2010

Edit1: я понимаю, что этот вопрос трудно понять без понимания того, что я пытаюсь сделать. Класс A не завершен, но он по существу обозначает C-массив «proxy» (или «viewer» или «sampleler»). Одно интересное использование также представляет C-массив в виде 2d-сетки (соответствующая функция здесь не показана). Свойство этого класса:

  • данные не должны владеть - нет глубокого копирования
  • должно быть копируемым / присваиваемым
  • должно быть легким (
  • это должно сохранить постоянство (у меня проблемы с этим)

Пожалуйста, не ставьте под сомнение цель или замысел: они являются гипотезой вопроса.

Первый код:

class A
{
private:
    float* m_pt;
public:
    A(float* pt)
        :m_pt(pt)
    {}
    const float* get() const
    {
        return m_pt;
    }
    void set(float pt)
    {
        *m_pt = pt;
    }
};

void gfi()
{
    float value = 1.0f;
    const A ac(&value);
    std::cout<<(*ac.get())<<std::endl;
    A a = ac;
    a.set(2.0f);
    std::cout<<(*ac.get())<<std::endl;
}

При вызове "gfi" выдается следующий вывод:

1
2

Назначение a с ac - это дешевый способ сокращения константности ac . Есть ли лучший способ защитить значение, на которое m_pt указывает?

Обратите внимание, что я действительно хочу, чтобы мой класс был копируемым / назначаемым, я просто не хочу, чтобы он терял свою постоянство в процессе.

Edit0: я также хочу иметь указатель там, и нет глубокого копирования, пожалуйста (скажем, указатель может быть гигантским массивом).

Edit2: благодаря ответам я пришел к выводу, что полезно иметь «конструктор const» (по крайней мере, в этом контексте). Я посмотрел и, конечно, я не тот, кто пришел к такому выводу. Вот интересная дискуссия: http://www.rhinocerus.net/forum/language-c-moderated/569757-const-constructor.html

Edit3: Наконец-то есть то, чем я доволен. Спасибо за вашу помощь. Дальнейшие отзывы более чем приветствуются

template<typename T>
class proxy
{
public:
    typedef T elem_t;
    typedef typename boost::remove_const<T>::type elem_unconst_t;
    typedef typename boost::add_const<T>::type elem_const_t;
public:
    elem_t* m_data;
public:
    proxy(elem_t* data = 0)
        :m_data(data)
    {}
    operator proxy<elem_const_t>()
    {
        return proxy<elem_const_t>(m_data);
    }
}; // end of class proxy

void test()
{
    int i = 3;
    proxy<int> a(&i);
    proxy<int> b(&i);
    proxy<const int> ac(&i);
    proxy<const int> bc(&i);
    proxy<const int> cc = a;
    a=b;
    ac=bc;
    ac=a;
    //a=ac; // error C2679: binary '=' : no operator found which takes a right-hand operand of type...
    //ac.m_data[0]=2; // error C3892: 'ac' : you cannot assign to a variable that is const
    a.m_data[0]=2;
}

Ответы [ 8 ]

2 голосов
/ 27 января 2010

Ваш класс плохо продуман:

  • он должен использовать значения с плавающей точкой, а не указатели
  • если вы хотите использовать указатели, вам, вероятно, нужно распределить их динамически
  • и затем вам нужно предоставить вашему классу конструктор копирования и оператор присваивания (и деструктор), который решит проблему

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

1 голос
/ 27 января 2010

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

Проблема, с которой вы столкнулись, была решена для итераторов, вам следует прочитать документацию по созданию собственных пар iterator и const_iterator, чтобы узнать, как это сделать.,

Примечание: в общем случае const iterator - это итератор, который не может быть увеличен / уменьшен, но может изменить значение, на которое он указывает.Где const_iterator - это другой класс, который может быть увеличен / уменьшен, но значение, на которое он указывает, не может быть изменено.

Это то же самое, что и разница между const float * и float *const.В вашем случае A - это то же самое, что float *, а const A - это то же самое, что и float *const.

Мне кажется, ваш выбор:

  • Инкапсулируйте вашdata.
  • Создайте отдельный класс const_A, как это делают итераторы
  • Создайте собственный конструктор копирования, который не допускает копирование const A, например, с подписью A(A & a);
1 голос
/ 27 января 2010

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

0 голосов
/ 27 января 2010

Вы можете запретить конструктору копирования определенные комбинации аргументов:

Например, добавление конструктора;

A(A& a) :m_pt(a.m_pt) { m_pt = a.m_pt; }

предотвращает инициализацию любого экземпляра A с const A.

Это также предотвращает const A a2 = a1, где a1 является const, но вам все равно не нужно делать это, так как вы можете просто использовать a1 напрямую - это const, даже если вы можете сделать копию, a2 всегда будет идентичен a1.

0 голосов
/ 27 января 2010

РЕДАКТИРОВАТЬ: рассматривая этот вопрос еще немного, я думаю, что вы неправильно истолковываете влияние const -корректности на указатели членов.Рассмотрим следующий удивительный пример:

//--------------------------------------------------------------------------------
class CNotSoConstPointer
 {
 float *mp_value;

 public:
   CNotSoConstPointer(float *ip_value) : mp_value(ip_value) {}

   void ModifyWithConst(float i_value) const
     {
     mp_value[0] = i_value;
     }

   float GetValue() const
     {
     return mp_value[0];
     }
 };

//--------------------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[])
  {
  float value = 12;
  const CNotSoConstPointer b(&value);
  std::cout << b.GetValue() << std::endl;

  b.ModifyWithConst(15);
  std::cout << b.GetValue() << std::endl;

  while(!_kbhit()) {}
  return 0;
  }

Это выдаст 12, а затем 15, не будучи "умным" относительно правильности const не очень-const объекта const.Причина в том, что только указатель сам по себе является константой, а не память, на которую он указывает.

Если последний - то, что вы хотите, вам понадобится намного больше обёрток, чтобы получить желаемое поведение, как в моемисходное предложение ниже или предложение Iain.

ОРИГИНАЛЬНЫЙ ОТВЕТ:


Вы можете создать шаблон для вашего массива-прокси, специализирующийся на const-массивах для const-версии.Специализированная версия будет иметь const * m_pt, возвращать указатель const, выдавать ошибку при попытке установить и т. Д.

Редактировать: Примерно так:

template<typename T>
class TProxy
  {
  T m_value;

  public:
    TProxy(T i_t) : m_value(i_t) {};

    template<typename T>
    TProxy(const TProxy<T> &i_rhs) : m_value(i_rhs.m_value) {}

    T get() { return m_value; }
    void set(T i_t) { m_value = i_t; }
  };

template<typename T>
class TProxy<const T *>
  {
  const T *mp_value;

  public:
    TProxy(const T *ip_t) : mp_value(ip_t) {};

    template<typename T>
    TProxy(const TProxy<T> &i_rhs) : m_value(i_rhs.mp_value) {}

    T get() { return m_value; }    
  };
0 голосов
/ 27 января 2010

Я думаю, вы должны использовать глубокое копирование и определить свой собственный оператор присвоения и конструктор копирования.

Кроме того, возвращать дескриптор внутренней структуры данных не рекомендуется.

0 голосов
/ 27 января 2010

const это всегда просто подсказка компилятору; нет способов сделать переменную постоянно доступной только для чтения.

0 голосов
/ 27 января 2010

Почему бы не заменить float* на float в A. Если у вас нет ни первоначального владельца float, который могут поменять ссылки float*, ни того, кто готов сделать изменяемое приведение к возвращаемое значение от a::get.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...