Here is a concrete example:
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; };
This is a very simple string type that does not allow you to modify the string to which it refers. CField adds the Field class by caching the string length.
Now, in action:
void foo(CField& cf) { // 0 cf = "foo"; // 1 Field& f = cf; // 2 f = "foobar"; // 3 }
What's happening?
- line 0: unknown state
- line 1: calling
CField& CField::operator=(CField const&) , which creates a temporary CField (using the constructor), cf.string is "foo" and cf.length is 3 - line 2: the same object, accessed through
Field directly - line 3: calling
Field& Field::operator=(Field const&) , which creates a temporary Field (using the constructor), cf.string is "foobar" and cf.length is 3 (no change)
As you can see, the invariant that length caches the length of the string is corrupted due to insecure access to the string via Field& .
source share