Чрезмерное использование mutable для повышения безопасности?

Предположим, у меня есть класс с массивом указателей, и у меня есть метод, который разыменовывает указатель и возвращает его в качестве ссылки. Я хочу позволить вызывающему методу вызывать неконстантные методы объекта, на который указывает указатель, но также хочу защитить себя от вызывающего, меняющего то, на что указывает указатель. Если я возвращаю ссылку на const, я должен пометить многие методы объекта-указателя как const, и, следовательно, многие из его переменных-членов класса как изменяемые.

  1. Это плохая практика? Если так, как я могу обойти это?
  2. Есть ли снижение производительности за чрезмерное использование mutable?

Пример:

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

0

Решение

Чтобы расширить то, что я говорил, нет смысла злоупотреблять mutable за это. Если это все, что вы хотите предотвратить:

*p = /* ... */;

тогда это можно сделать гораздо проще, удалив оператор присваивания Counter:

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

Помните, что оператор присваивания не влияет на идентичность объекта: он не меняет его адрес. Семантически, задание, включающее в себя изменение this объект для воспроизведения состояния другого объекта. По факту, даже если вы запрещаете мне использовать оператор присваивания каким-либо образом, Я все еще мог сделать это:

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

Это дает тот же результат, что и выполнение задания, хотя и очень неуклюже и неэффективно.

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

1

Другие решения


По вопросам рекламы [email protected]