Rule A3-8-1 (required, implementation, not automated)
An object shall not be accessed outside of its lifetime.
Rationale
Accessing an object outside of its lifetime, i.e. before its initialization or constructor has completed, or after its non-trivial destructor has finished, is well defined only for a very restricted number of cases, as laid out by the language standard. Outside of these cases it leads to undefined behavior. Note: The AUTOSAR C++14 guidelines contain several other rules which are special cases of this rule (see references below). This rule was added to provide generic coverage for all cases not contained in these specialized rules. This also makes it easier to provide tracing from other standards with a similar generic rule. Note: The examples given below are not intended to represent a complete list of situations where violations of this rule can occur.
Example
//% $Id: A3-8-1.cpp 305786 2018-01-30 08:58:33Z michal.szczepankiewicz $
//
// 1. Pointer to virtual base is passed as function argument after lifetime of
// object has ended.
//
class B
{
};
class C1 : public virtual B // violates M10-1-1
{
};
class C2 : public virtual B // violates M10-1-1
{
};
class D : public C1, public C2
{
};
void f(B const* b){};
void example1()
{
D* d = new D(); // lifetime of d starts (violates A18-5-2)
// Use d
delete d; // lifetime of d ends (violates A18-5-2)
f(d); // Non-compliant - Undefined behavior, even if argument is not used
// by f().
}
//
// 2. Accessing an initializer_list after lifetime of initializing array has
// ended.
//
class E
{
std::initializer_list<int> lst;
public:
// Conceptually, this works as if a temporary array {1, 2, 3} was created
// and a reference to this array was passed to the initializer_list. The
// lifetime of the temporary array ends when the constructor finishes.
E() : lst{1, 2, 3} {}
int first() const { return *lst.begin(); }
};
void example2()
{
E e;
std::out << e.first() << "\n"; // Non-compliant
}
//
// 3. Exiting main while running tasks depend on static objects
//
void initialize_task()
{
// start some task (separate thread) which depends on some static object.
// ...
}
int main()
{
// static constructors are called
initialize_task();
} // main ends, static destructors are called
// Non-compliant
// Task begins to run and accesses destroyed static object.
//
// 4. Storage reuse without explicit destructor call
//
void example4()
{
std::string str;
new (&a) std::vector<int>{}; // Non-compliant: storage of str reused without
// calling its non-trivial destructor.
} // Non-compliant: Destructor of str is implicitly called at scope exit, but
// storage contains object of different type.
See also
ISO/IEC 14882:2014 [3]: 3.8: [basic.life] JSF December 2005 [8]: AV Rule 70.1: An object shall not be improperly used before its lifetime begins or after its lifetime ends. SEI CERT C++ Coding Standard [10]: EXP54-CPP: Do not access an object outside of its lifetime. A5-1-4 in section 6.5.1 M7-5-1 in section 6.7.5 M7-5-2 in section 6.7.5 A7-5-1 in section 6.7.5 M12-1-1 in section 6.12.1