Recently, I found an interesting discussion on how to allow read-only access to private members without obfuscating the design with multiple getters, and one of the suggestions was to do it this way:
#include <iostream>
class A {
public:
A() : _ro_val(_val) {}
void doSomething(int some_val) {
_val = 10*some_val;
}
const int& _ro_val;
private:
int _val;
};
int main() {
A a_instance;
std::cout << a_instance._ro_val << std::endl;
a_instance.doSomething(13);
std::cout << a_instance._ro_val << std::endl;
}
Output:
$ ./a.out
0
130
GotW#66 clearly states that object’s lifetime starts
when its constructor completes successfully and returns normally. That is, control reaches the end of the constructor body or an earlier return statement.
If so, we have no guarantee that the _val memeber will have been properly created by the time we execute _ro_val(_val). So how come the above code works? Is it undefined behaviour? Or are primitive types granted some exception to the object’s lifetime?
Can anyone point me to some reference which would explain those things?
Before the constructor is called an appropriate amount of memory is reserved for the object on Freestore(if you use
new) or on stack if you create object on local storage. This implies that the memory for_valis already allocated by the time you refer it in Member initializer list, Only that this memory is not properly initialized as of yet.Makes the reference member
_ro_valrefer to the memory allocated for_val, which might actually contain anything at this point of time.There is still an Undefined Behavior in your program because, You should explicitly initialize
_valto0(or some value,you choose)in the constructor body/Member Initializer List.The output0in this case is just because you are lucky it might give you some other values since_valis left unInitialized. See the behavior here on gcc 4.3.4 which demonstrates the UB.But as for the Question, Yes indeed the behavior is Well-Defined.