I was looking into some code and it seemed to work but I am not sure if it is defined behaviour.
I think there is a problem in it because Base is constructed with a reference to a Derived member variable - wich is in my understanding constructed after Base.
I did some research on this and all I found were answers stating that Base is constructed before Derived, and Arguments from a Derived Constructor can be forwareded to the Base constructor.
But what about Derived Members, can they safely be forwareded to a Base Constructor? Might Constructor and Destructor or even Member-Functions in Base work with invalid objects?
Here is simple code sample with the problem:
class Base
{
public:
Base(SomeClass & obj): m_obj(obj)
{
// Does using m_obj here cause problems with a Derived instance?
}
virtual ~Base()
{
// Does using m_obj here cause problems with a Derived instance?
}
void SomeMethod()
{
// Does using m_obj here cause problems with a Derived instance?
}
private:
SomeClass & m_obj;
};
class Derived : public Base
{
public:
Derived():Base(m_derObj){}
private:
SomeClass m_derObj{123};
};
Maybe I am missing some gurantee C gives - or maybe we were just lucky and the error never occured.
CodePudding user response:
Yes, your hunch is correct.
Using an object before it has been constructed, or after it has been destroyed, has undefined behaviour, and the worst type of undefined behaviour is the appearance of working properly.
(You can't conclude from watching a C program do what it's supposed to do that it doesn't have undefined behaviour.)
The constructor can store the reference but not use the referred object in any way, since that object's lifetime hasn't started yet.
The destructor can't use the object since its lifetime has already ended.
You can use the referred object in other member functions during its lifetime in the regular way.
This lifetime issue is Yet Another Reason to avoid references as members.
CodePudding user response:
Is using a reference to a derived class member in its base class well defined in C ?
Yes.
SomeClass m_derObj(123);
This is invalid syntax. If you intended to write a default member initialiser, you must use either curly braces or equals initialiser.
Base(SomeClass & obj): m_obj(obj)
{
// Does using m_obj here cause problems with a Derived instance?
}
The referred object hasn't been initialised yet, so you are very limited in what you can do with the reference.
If you try to do something that isn't allowed for such reference, such as for example try to access the referred object, then the behaviour of the program is undefined. If you don't do such thing, then it's OK.
virtual ~Base()
{
// Does using m_obj here cause problems with a Derived instance?
}
The referred object has been destroyed already, so you are very limited in what you can do with the reference.
If you try to do something that isn't allowed for such reference, such as for example try to access the referred object, then the behaviour of the program is undefined. If you don't do such thing, then it's OK.
void SomeMethod()
{
// Does using m_obj here cause problems with a Derived instance?
}
Depending on which constructor you used to initialise the object, there is a possibility that the reference has become dangling. In such case, there won't be anything that you can do with the reference without causing undefined behaviour. Furthermore, if you call the function from the constructor or destructor, the issue with the object being destroyed/not yet constructed applies.
But in the case the reference is still valid, it's fine.