You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 40 Next »

Reallocation can occur when a member function modifies its container. Modifying member functions include reserve() and resize(), push_back(), pop_back(), erase(), clear(), insert(), and others. In addition, assignment operations and modifying algorithms can also cause reallocation. When a container reallocates its elements, their addresses change. This applies only to contiguous-memory iterators, in particular std::vector, std::deque and std::string.

In general iterators on node-based containers such as std::list are not invalidated via modification of the container, unless the element referenced by the iterator is itself erased.

Pointers and references to objects within a container are also invalidated when iterators are invalidated. A single exception applies for the std::deque class: it preserves pointers and references to internal objects upon inserts to either its beginning or its end, but it does not preserve iterators.

Consequently, the values of existing iterators may be invalidated [Kalev 99]. Using invalid iterators yields undefined results.

Non-Compliant Code Example

In this example, the iterator pos is invalidated after the call to insert, and subsequent loop iterations have undefined behavior.

double data[5] = { 2.3, 3.7, 1.4, 0.8, 9.6 };

deque<double> d;
deque<double>::iterator pos = d.begin();

for (size_t i = 0; i < 5; ++i) {
  d.insert(pos++, data[i] + 41);
}

Compliant Solution 1

Update pos each time insert is called to keep the iterators valid, and then increment it:

double data[5] = { 2.3, 3.7, 1.4, 0.8, 9.6 };

deque<double> d;
deque<double>::iterator pos = d.begin();

for (size_t i = 0; i < 5; ++i) {
  pos = d.insert(pos, data[i] + 41);
  ++pos;
}

Compliant Solution 2

Use one of the STL algorithms.

double data[5] = { 2.3, 3.7, 1.4, 0.8, 9.6 };
deque<double> d;

transform(data, data+5,
    inserter(d, d.begin()),
    bind2nd(plus<int>(), 41));

Non-compliant Code Example (std::string::data())

In this non-compliant code example, data is to be considered out-of-scope just after the call to replace.

const char *data = example_string.data();
example_string.replace(0, 2, "bb");
char val = data[1];

Compliant Solution (std::string::data())

In this compliant solution, the data array is created after modifications to the example string are complete. The modification makes the code compliant: replace is called before the call to data().

const char *data = example_string.data();
example_string.replace(0, 2, "bb");
data = example_string.data();
char val = data[1];

Risk Assessment

Using invalid iterators yields undefined results.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CTR32-CPP

High

Probable

High

P6

L2

Bibliography

[Meyers 01] Item 43: Prefer algorithm calls to hand-written loops.
[Sutter 04] Item 84: Prefer algorithm calls to handwritten loops.
[Kalev 99] ANSI/ISO C++ Professional Programmer's Handbook.
[ISO/IEC 14882-2003] Section 24: Iterators Library.


      06. Containers (CTR)      CTR33-CPP. Guarantee that copies are made into storage of sufficient size

  • No labels