开发者

const and pointers

Edit1: I realize this is hard to understand this question without having an insight of what I'm trying to do. The class A is not complete but it essentially stand for a C-array "proxy" (or "viewer" or "sampler"). One interesting usage is too present a C-array as a 2d grid (the relevant function are not shown here). The property of this class are the following:

  • it should not own the data - no deep copyy
  • it should be copyable/assignable
  • it should be lightweight (
  • it should preserve constness (I'm having trouble with this one)

Please do not question the purpose or the design: they are the hypothesis of the question.

First some code:

class A
{
private:
    float* m_pt;
public:
    A(float* pt)
        :m_pt(pt)
    {}
    const float* get() const
    {
        return m_pt;
    }
    void set(float pt)
    开发者_如何学Go{
        *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;
}

Calling "gfi" generate the following output:

1
2

Assigning a with ac is a cheap way to shortcut the constness of ac. Is there a better way to protect the value which m_pt point at?

Note that I DO want my class to be copyable/assignable, I just don't want it to loose its constness in the process.

Edit0: I also DO want to have a pointer in there, and no deep copy please (let say the pointer can be a gigantic array).

Edit2: thanks to the answers, I came to the conclusion that a "const constructor" would be a useful thing to have (at least in this context). I looked it up and of course I'm not the same one who reached this conclusion. Here's an interesting discussion: http://www.rhinocerus.net/forum/language-c-moderated/569757-const-constructor.html

Edit3: Finally got something which I'm happy with. Thanks for your help. Further feedback is more than welcome

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;
}


Your class is badly designed:

  • it should use float values, not pointers
  • if you want to use pointers, you probably need to allocate them dynamically
  • and then you need to give your class a copy constructor and assignment operator (and a destructor) , which will solve the problem

Alternatively, you should prevent copying and assignment by making the copy constructor and assignment op private and then not implementing them.


You can trick around with proxy pattern and additional run-time constness boolean member. But first, please tell us why.


Effectively your class is like an iterator that can only see one value. It does not encapsulate your data just points to it.

The problem you are facing has been solved for iterators you should read some documentation on creating your own iterator and const_iterator pairs to see how to do this.

Note: in general a const iterator is an iterator that cannot be incremented/decremented but can change the value it points to. Where as a const_iterator is a different class that can be incremented/decremented but the value it points to cannot be changed.

This is the same as the difference between const float * and float *const. In your case A is the same as float * and const A is the same as float *const.

To me your choices seem to be:

  • Encapsulate your data.
  • Create a separate const_A class like iterators do
  • Create your own copy constructor that does not allow copies of const A eg with a signature of A(A & a);


EDIT: considering this question some more, I think you are misinterpreting the effect of const-correctness on member pointers. Consider the following surprising example:

//--------------------------------------------------------------------------------
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;
  }

This will output 12 and then 15, without ever being "clever" about the const-correctness of the const not-so-const object. The reason is that only the pointer ITSELF is const, not the memory it points to.

If the latter is what you want, you'll need a lot more wrapping to get the behavior you want, like in my original suggestion below or Iain suggestion.

ORIGINAL ANSWER:


You could create a template for your array-proxy, specialized on const-arrays for the const version. The specialized version would have a const *m_pt, return a const pointer, throw an error when you try to set, and so on.

Edit: Something like this:

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; }    
  };


Why not replace float* with float in A. If you don't either the original owner of the float that the float* references can change it, or anyone prepared to do a mutable cast on the return value from a::get.


const is always just a hint to the compiler; there are no ways to make a variable permanently read-only.


I think you should use deep copy and define your own assingment operator and copy constructor.

Also to return handle to internal data structure in not a good practice.


You can deny the copy-constructor for certain combinations of arguments:

For instance, adding the constructor;

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

prevents any instance of A being initialised with a const A.

This also prevents const A a2 = a1 where a1 is const, but you should never need to do this anyway, since you can just use a1 directly - it's const even if you could make a copy, a2 would be forever identical to a1.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜