Hi,
I've got a long hate relationship with this warning, because it kept
popping up and never showed a real problem in my code. Some more
objective arguments against can be found inline.
Nikolai Pretzell wrote:
> warning:
test.cxx(18) : warning C4355: 'this' : used in base member initializer
list
class A
{
public:
A() {}
};
class B
{
A& _a;
public:
B( A& a ) : _a(a) {}
};
class C : public A
{
B _b;
public:
C() : _b( *(A*)this ) {}
BTW: Why this cast? Surely using the implicit conversion is fine. And if
you insist on pointing out what happens, use static_cast.
};
int main()
{
C c;
}
Using 'this' in the ctor initializer list can be dangerous, if you
don't know what you do. The above example uses a safe way to provide
this.
This example does not look too safe to me, because:
It is quite possible that - for example - the maintainer of B one day
wants to use a virtual funtion of A that is implemented in C.
There are no virtual functions in A that can be abused :-o [That
probably means the inheritance should not be public, but that is a
different issue].
But this points out that there is only a problem:
1. if A has virtual interfaces that B might want to use
2a. if B does a lot of work during construction,
which might involve calling members of its argument OR
2b. the constructor of C uses B in ways,
which might make B call members of the constructor argument
3. if B is maintained independently of A
In my experience passing 'this' to member or base class constructors is
most often done with classes that are designed for this purpose so it is
part of their contract that you can do that. Often these classes are
designed for close collaboration and so are not even maintained
independently.
And that functionality of B may be called in the constructor body of C.
Which highlights that use of 'this' in the constructor body, where more
complex operations may happen, is at least as dangerous as using it in
the initializer list. And it is subject to the same problems, for
example if C is not the most derived class.
And IMHO this condition (2b above) is very specific. The rule would be:
"don't pass 'this' to a subobject and then do complex things with that
subobject in your constructor". But there is no warning for this rule
(which is good advice), but for a much wider range of cases including
perfectly safe ones.
And btw: In the example C does not call members of this->_b in the
constructor body.
Pro warning:
In cases like this, generally the maintainer of B has no chance to know
that he must not use some functions of A (or C), because the instance of
A (or C) is not yet completely constructed.
I think it's the reverse. The author of C should not do this unless this
way of using it is part of the design of B. But if it is, this use is a
valid use case. And it is not easy to replace by a safer usage pattern.
So the warning is a valid hint to a potential maintainability nightmare.
Relying on behaviors that are not documented is a common cause of
maintainability problems. In general you cannot warn about them. I don't
find this case to be so special, that it warrants a compile error.
Contra warning:
The normal workaround would be to move the "this" from the
initialization section into the constructor body. This removes the
warning, but not always removes the danger, because still the receiver
of "this" may use it while not all construction is done.
In order to be able to move use of 'this' to the constructor body, the
object being initialized needs to support a two-stage initialization:
first default-initialize to an invalid state and then finish
initialization through a member function. During a init() function I'm
much more inclined to assume that all my members are fully usable. So
doing only this may even increase the risks. So now you'd have to add
two-stage initialization to C as well. But even that does not solve the
problem that C:init() -> B:init() -> C:member() might invoke C
frunctionality before C is ready.
In many cases two-stage initialization creates more problems than it
solves, because now a class has a weaker invariant and needs to check
whether it is valid all the time and clients must be able to handle the
errors reported by an invalid instance.
So one could argue, fixing the warning does not necessarily fix the
problem, so we can remove the warning anyway.
Right. The warning (with warnings=errors) forces us to use a workaround,
which may even have a negative impact on the design (and thus
maintainability). And this workaround avoids one potentially risky
practice by using an equally risky one which happens to be not warned about.
The one thing that the warning does, it that it makes you think about
this risk when you encounter it. But once you have become accustomed to
simply use the workaround design from the outset, that wears off
although the problem still lingers.
I think we have two possible ways to solve this problem.
- Turn of the warning for every occurrence in the code.
- Switch of C4355 for Windows globally.
I'd suggest to take the warning serious and to re-look on each
occurrence about this way:
1. Can the construction be re-worked so no self-reference is necessary?
Sometimes this is easy by clarifying a hierarchy that could be a
directed graph but currently is a non-directed one.
If yes, do so.
2. If not, is the use of "this" safe including maintainenance in later
times?
If yes, see X.
3. If no, make sure there is a comment at all places, using the
partly-initialized instance that it may be only partly initialized!
Then goto X.
X. See, if moving "this" from initialization section into c'tor body
does any harm. (In case of dialogues, the painting takes so much time,
that one could assume, the minor performance drawback of moving the
initialisation into the body is negligible - but I don't know that code,
so it's just an assumption.)
I don't know about dialogs, but in general the ability to move this into
the c'tor body requires a redesign of the classes used. For example a
reference meber may need to become a pointer and suddenly you have to
check for NULL on all uses.
If yes, do so.
If no, disable the warning locally.
I find this ugly. And if we go this we we should look for a way to use a
platform independent, readable way to disable it inline with the code
(e.g. a SAFE_USE_OF_THIS_IN_CTOR_BEGIN macro) instead of sprinkling
platform-specific stuff all over the code (or makefiles).
If we just generally disable the warning, we may overlook occurrences
where the use indeed is questionable.
As mentioned before, I don't think this case is special enough to
warrant all this overhead when equally dangerous, similar cases are left
without warning.
There can be a coding/code review rule about self-referentiality and use
of 'this' in a constructor context and maybe another one about one- vs.
two-stage (de)initialization. But as the warning cannot check the entire
rule, it may even be detrimental, because it creates a false sense of
security (the compiler detects violations of this, so I don't need to
check myself).
--
Joerg Barfurth Sun Microsystems - Desktop - Hamburg
>>>>>>>>>>>>>>>>>> using std::disclaimer <<<<<<<<<<<<<<<<<<<<<<<
Software Engineer [EMAIL PROTECTED]
Thin Client Software
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]