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
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
@@ -4671,6 +4671,7 @@ Concrete type rule summary:
4671
4671
* [C.10: Prefer concrete types over class hierarchies](#rc-concrete)
4672
4672
* [C.11: Make concrete types regular](#rc-regular)
4673
4673
* [C.12: Don't make data members `const` or references in a copyable or movable type](#rc-constref)
4674
+
* [C.13: If one data member uses another, declare it before the other](#rc-lifetime)
4674
4675
4675
4676
4676
4677
### <a name="rc-concrete"></a>C.10: Prefer concrete types over class hierarchies
@@ -4792,6 +4793,102 @@ If you need a member to point to something, use a pointer (raw or smart, and `gs
4792
4793
Flag a data member that is `const`, `&`, or `&&` in a type that has any copy or move operation.
4793
4794
4794
4795
4796
+
### <a name="rc-lifetime"></a>C.13: If one data member uses another, declare it before the other
4797
+
4798
+
##### Reason
4799
+
4800
+
Data members are initialized in the order they are declared, and destroyed in the reverse order.
4801
+
4802
+
##### Discussion
4803
+
4804
+
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.
4805
+
4806
+
##### Example; bad
4807
+
4808
+
// Bad: b uses a, but a is declared after b.
4809
+
// Construction order is b then a; destruction order is a then b.
4810
+
// So b touches a outside a's lifetime.
4811
+
4812
+
class X {
4813
+
struct B {
4814
+
string* p;
4815
+
explicit B(string& a) : p(&a) {}
4816
+
~B() { cout << *p; } // uses a (via p)
4817
+
};
4818
+
4819
+
B b; // constructed first
4820
+
string a = "some heap allocated string value"; // constructed after b; destroyed before b
4821
+
4822
+
public:
4823
+
X() : b(a) {} // uses a before it is constructed -> use-before-alloc UB
4824
+
~X() = default; // accesses a after it is destroyed -> use-after-free UB
4825
+
};
4826
+
4827
+
##### Example; good
4828
+
4829
+
// Corrected: Just declare a before b
4830
+
4831
+
class X {
4832
+
struct B {
4833
+
string* p;
4834
+
explicit B(string& a) : p(&a) {}
4835
+
~B() { cout << *p; } // uses a (via p)
4836
+
};
4837
+
4838
+
string a = "some heap allocated string value"; // constructed before b; destroyed after b
4839
+
B b; // constructed second
4840
+
4841
+
public:
4842
+
X() : b(a) {} // ok
4843
+
~X() = default; // ok
4844
+
};
4845
+
4846
+
##### Example; bad
4847
+
4848
+
This can also come up with concurrency. Ensure that an async operation that access a value is joined before the value it accesses is destroyed.
0 commit comments