0

Suppose I have a class that has an array of pointers, and I have a method that dereferences a pointer and returns it as a reference. I want to allow the method caller to call non-const methods of the object the pointer is pointing to, but also want to protect myself from the caller changing what the pointer is pointing to. If I return a const reference, I have to mark many of the pointer object's methods as const, and hence many of its class member variables as mutable.

  1. Is this bad practice? If so, how do I get around this?
  2. Is there performance penalty for over-using mutable?

Example:

#include <iostream>
#include <array>
#include <memory>

class Counter
{
public:
  Counter();
  void hit() const;
  void reset();
  unsigned count() const;
private:
  mutable unsigned count_;
};

Counter::Counter() : count_(0) {}

void Counter::hit() const { ++count_; }

void Counter::reset() { count_ = 0; } 

unsigned Counter::count() const { return count_; }

class CircularArray
{
public:
  CircularArray();
  const Counter& next() const;
private:
  mutable unsigned i_;
  std::array<std::unique_ptr<Counter>, 3> arr_;
};

CircularArray::CircularArray() : i_(2)
{
  arr_[0] = std::unique_ptr<Counter>(new Counter);
  arr_[1] = std::unique_ptr<Counter>(new Counter);
  arr_[2] = std::unique_ptr<Counter>(new Counter);
}

const Counter& CircularArray::next() const { return *arr_[(i_ = (i_ + 1) % 3)]; }

int main()
{
  CircularArray circular;
  const Counter* p;
  p = &circular.next();

  p->hit();
  p->hit();

  Counter c;
  //*p = c; // <-- Want to prevent this
}
Agrim Pathak
  • 3,047
  • 4
  • 27
  • 43
  • 1
    Instead of *abusing* `mutable` for this purpose, why not just delete the assignment operator? – Rufflewind Dec 04 '14 at 05:16
  • The assignment operator is just another member function. There is nothing special about it. It modifies `*this` exactly like any other non-const member function. Why single it out? If it is inappropriate for public use, just don't make it public. – n. m. could be an AI Dec 04 '14 at 06:02
  • I am a little confused by the question. You have your CircularArray which contains ptrs to an array of Counters. You want to allow non-const access to one of the Counter individuals. I am correct in thinking that what you are trying to avoid is that the caller does not replace one of the array elements altogether with another instance? ("caller changing what the pointer is pointing to"). They would not be able to do that anyway, you are not returning a pointer to the pointer – harmic Dec 04 '14 at 06:08
  • @Rufflewind Thanks, that works and is a clean solution. I'm open to any other ideas as well. – Agrim Pathak Dec 04 '14 at 06:15
  • @harmic If next() doesn't return a const reference (meant to say reference in OP, fixed now), then *p = c is possible and the element the pointer was pointing to is replaced. – Agrim Pathak Dec 04 '14 at 06:17

1 Answers1

1

To extend what I was saying, there is no point in abusing mutable for this. If this is all you want to prevent:

*p = /* ... */;

then it can be done much more easily by deleting the assignment operator of Counter:

class Counter
{
    void operator=(const Counter&) = delete;
    // ...
};

Remember that the assignment operator does not affect the identity of the object: it doesn't change its address. Semantically, an assignment involving of modifying this object to replicate the state of another object. In fact, even if you forbid me from using the assignment operator somehow, I could still do this:

// a very inefficient way of performing `*p = c`
p->reset();
while (p->count() != c.count())
    p->hit();

This achieves the exact same result as performing an assignment, albeit very clumsily and inefficiently.

Performing an assignment is no different than calling a non-const member function that accepts a single argument of type const Counter&. Hypothetically, you could redefine the assignment operator to do absolutely nothing at all if you wanted to (it would be a bad idea though).

Rufflewind
  • 8,545
  • 2
  • 35
  • 55