Давайте приведем конкретный пример:
struct Field {
Field(char const* s): string(s) {}
char const* string;
};
struct CField: Field {
CField(char const* s): Field(s), length(s ? ::strlen(s) : 0) {}
std::size_t length;
};
Это очень простой тип строки, который также не позволяет изменять строку, на которую он ссылается. CField
увеличивает класс Field
, кэшируя длину строки.
Сейчас в действии:
void foo(CField& cf) { // 0
cf = "foo"; // 1
Field& f = cf; // 2
f = "foobar"; // 3
}
Что происходит?
- строка 0: состояние неизвестно
- строка 1: вызов
CField& CField::operator=(CField const&)
, который создает временный CField
(с использованием конструктора), cf.string
равен "foo"
и cf.length
равен 3
- строка 2: тот же объект, доступ через
Field
напрямую
- строка 3: вызов
Field& Field::operator=(Field const&)
, который создает временное Field
(с использованием конструктора), cf.string
равно "foobar"
и cf.length
равно 3
(без изменений)
Как вы можете заметить, инвариант, который length
кэширует длину string
, нарушен из-за незащищенного доступа к string
через Field&
.