C ++ Уязвимость к правильности или непреднамеренное использование?

Я что-то упустил, или 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??! Или это ожидаемое поведение или просто плохое использование? Если это один из последних, оцените, если вы можете определить, как это можно исправить?

Спасибо за ваше время и интерес.

1

Решение

Это не новое открытие, вы можете прочитать что-то вроде «const is shallow» в C ++. Что приводит к естественному различию между физическим и логический const (читайте после второго тоже).

Если у вас есть указатель в классе, будь он умным или глупым, вы, вероятно, вовлечены в эту проблему и должны тщательно спроектировать. Принимая во внимание, что изменение прикрепленных данных на другом конце указателя не обнаружено.

Возможные обходные пути — сделать указатель const T * и добавить закрытую функцию-член, которая возвращает T *. Другой способ состоит в том, чтобы ограничить прямой доступ к указателю в целом, и требовать пары функций: одна const, а другая — nonconst.

2

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

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 объекта.

2