Я что-то упустил, или const-правильность не работает так, как предполагалось с указателями (или, может быть, умными указателями, так как это то, что я проверял?). Во всяком случае, вот что я наблюдал, пробуя вариант идиомы PIMPL.
Я объявил следующее:
class A {
public:
A(...);
...
bool update_stuff(...) const;
...
protected:
bool update_stuff_impl(...) const;
...
private:
struct pimpl;
pimpl* m_pimpl;
};
А для реализации у меня есть что-то вроде:
struct A::pimpl {
pimpl(...): some_data(new some_type());
...
some_method1(...); // Modifies content of some_data
some_method2(...); // Modifies content of some_data
...
boost::shared_ptr<some_type> some_data;
};
A::A(...): m_pimpl(new pimpl(...)) {
...
}
bool A::update_stuff(...) const {
if( !update_stuff_impl(...) ) {
return false;
}
return true;
}
bool A::update_stuff_impl(...) const {
//-Change content of pimpl::some_data here
m_pimpl->some_method1(...);
m_pimpl->some_method2(...);
return true;
}
Что мне трудно понять, так это то, как я мог бы сойти с рук с помощью const
классификатор для функций A::update_stuff(...)
а также A::update_stuff_impl(...)
когда я на самом деле модифицирую A::pimpl::some_data
??! Или это ожидаемое поведение или просто плохое использование? Если это один из последних, оцените, если вы можете определить, как это можно исправить?
Спасибо за ваше время и интерес.
Это не новое открытие, вы можете прочитать что-то вроде «const is shallow» в C ++. Что приводит к естественному различию между физическим и логический const (читайте после второго тоже).
Если у вас есть указатель в классе, будь он умным или глупым, вы, вероятно, вовлечены в эту проблему и должны тщательно спроектировать. Принимая во внимание, что изменение прикрепленных данных на другом конце указателя не обнаружено.
Возможные обходные пути — сделать указатель const T * и добавить закрытую функцию-член, которая возвращает T *. Другой способ состоит в том, чтобы ограничить прямой доступ к указателю в целом, и требовать пары функций: одна const, а другая — nonconst.
C ++ защищает постоянство pimpl* m_pimpl
переменная. Это не позволяет изменить значение указателя. Но это позволяет делать все с объектом, на который указывает указатель. Как правило, нет способа защитить его.
Например, рассмотрим переменные члена класса int a; int* b;
, В функции-члене класса мы можем сделать следующее:
int a_copy = a;
a_copy = 42;
int* b_copy = b;
*b_copy = 42;
Вот a_copy
а также b_copy
являются локальными переменными. Они не защищены константой объекта, в котором вы находитесь. Поэтому этот код можно выполнить с помощью метода const. Разница в том, что a
здесь значение переменной не изменилось, и *b
значение было изменено. Поскольку указатель может быть легко скопирован, у компилятора нет способа узнать, равен ли какой-либо указатель любому указателю, лежащему в значении элемента const объекта.