You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add C.13 to ensure data member lifetimes nest when needed... see #2316 (#2321)
* Add C.13 to ensure data member lifetimes nest when needed... see #2316Closes#2316
* Apply suggestion from @beinhaerter
Co-authored-by: Werner Henze <34543625+beinhaerter@users.noreply.github.com>
* Apply suggestions from code review
Co-authored-by: Werner Henze <34543625+beinhaerter@users.noreply.github.com>
* Apply wording and dictionary fixes
---------
Co-authored-by: Werner Henze <34543625+beinhaerter@users.noreply.github.com>
Copy file name to clipboardExpand all lines: CppCoreGuidelines.md
+97Lines changed: 97 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4672,6 +4672,7 @@ Concrete type rule summary:
4672
4672
* [C.10: Prefer concrete types over class hierarchies](#rc-concrete)
4673
4673
* [C.11: Make concrete types regular](#rc-regular)
4674
4674
* [C.12: Don't make data members `const` or references in a copyable or movable type](#rc-constref)
4675
+
* [C.13: If data member `B` uses another data member `A`, declare `A` before `B`](#rc-lifetime)
4675
4676
4676
4677
4677
4678
### <a name="rc-concrete"></a>C.10: Prefer concrete types over class hierarchies
@@ -4793,6 +4794,102 @@ If you need a member to point to something, use a pointer (raw or smart, and `gs
4793
4794
Flag a data member that is `const`, `&`, or `&&` in a type that has any copy or move operation.
4794
4795
4795
4796
4797
+
### <a name="rc-lifetime"></a>C.13: If data member `B` uses another data member `A`, declare `A` before `B`
4798
+
4799
+
##### Reason
4800
+
4801
+
Data members are initialized in the order they are declared, and destroyed in the reverse order.
4802
+
4803
+
##### Discussion
4804
+
4805
+
If data member `B` uses another data member `A`, then `A` must be declared before `B` so that `A` outlives `B`, meaning that `A`'s lifetime starts before and ends after `B`'s lifetime. Otherwise, during construction and destruction `B` will attempt to use `A` outside its lifetime.
4806
+
4807
+
##### Example; bad
4808
+
4809
+
// Bad: b uses a, but a is declared after b.
4810
+
// Construction order is b then a; destruction order is a then b.
4811
+
// So b touches a outside the lifetime of a.
4812
+
4813
+
class X {
4814
+
struct B {
4815
+
string* p;
4816
+
explicit B(string& a) : p{&a} {}
4817
+
~B() { cout << *p; } // uses a (via p)
4818
+
};
4819
+
4820
+
B b; // constructed first
4821
+
string a = "some heap allocated string value"; // constructed after b; destroyed before b
4822
+
4823
+
public:
4824
+
X() : b{a} {} // uses a before it is constructed -> use-before-alloc UB
4825
+
~X() = default; // accesses a after it is destroyed -> use-after-free UB
4826
+
};
4827
+
4828
+
##### Example; good
4829
+
4830
+
// Corrected: Just declare a before b
4831
+
4832
+
class X {
4833
+
struct B {
4834
+
string* p;
4835
+
explicit B(string& a) : p{&a} {}
4836
+
~B() { cout << *p; } // uses a (via p)
4837
+
};
4838
+
4839
+
string a = "some heap allocated string value"; // constructed before b; destroyed after b
4840
+
B b; // constructed second
4841
+
4842
+
public:
4843
+
X() : b{a} {} // ok
4844
+
~X() = default; // ok
4845
+
};
4846
+
4847
+
##### Example; bad
4848
+
4849
+
This can also come up with concurrency. Ensure that an async operation that accesses a value is joined before the value it accesses is destroyed.
0 commit comments