Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How bad is "if (!this)" in a C++ member function?

If I come across old code that does if (!this) return; in an app, how severe a risk is this? Is it a dangerous ticking time bomb that requires an immediate app-wide search and destroy effort, or is it more like a code smell that can be quietly left in place?

I am not planning on writing code that does this, of course. Rather, I've recently discovered something in an old core library used by many pieces of our app.

Imagine a CLookupThingy class has a non-virtual CThingy *CLookupThingy::Lookup( name ) member function. Apparently one of the programmers back in those cowboy days encountered many crashes where NULL CLookupThingy *s were being passed from functions, and rather than fixing hundreds of call sites, he quietly fixed up Lookup():

CThingy *CLookupThingy::Lookup( name )  {    if (!this)    {       return NULL;    }    // else do the lookup code... }  // now the above can be used like CLookupThingy *GetLookup()  {   if (notReady()) return NULL;   // else etc... }  CThingy *pFoo = GetLookup()->Lookup( "foo" ); // will set pFoo to NULL without crashing 

I discovered this gem earlier this week, but now am conflicted as to whether I ought to fix it. This is in a core library used by all of our apps. Several of those apps have already been shipped to millions of customers, and it seems to be working fine; there are no crashes or other bugs from that code. Removing the if !this in the lookup function will mean fixing thousands of call sites that potentially pass NULL; inevitably some will be missed, introducing new bugs that will pop up randomly over the next year of development.

So I'm inclined to leave it alone, unless absolutely necessary.

Given that it is technically undefined behavior, how dangerous is if (!this) in practice? Is it worth man-weeks of labor to fix, or can MSVC and GCC be counted on to safely return?

Our app compiles on MSVC and GCC, and runs on Windows, Ubuntu, and MacOS. Portability to other platforms is irrelevant. The function in question is guaranteed to never be virtual.

Edit: The kind of objective answer I am looking for is something like

  • "Current versions of MSVC and GCC use an ABI where nonvirtual members are really statics with an implicit 'this' parameter; therefore they will safely branch into the function even if 'this' is NULL" or
  • "a forthcoming version of GCC will change the ABI so that even nonvirtual functions require loading a branch target from the class pointer" or
  • "the current GCC 4.5 has an inconsistent ABI where sometimes it compiles nonvirtual members as direct branches with an implicit parameter, and sometimes as class-offset function pointers."

The former means the code is stinky but unlikely to break; the second is something to test after a compiler upgrade; the latter requires immediate action even at high cost.

Clearly this is a latent bug waiting to happen, but right now I'm only concerned with mitigating risk on our specific compilers.

like image 641
Crashworks Avatar asked Jan 12 '12 21:01

Crashworks


People also ask

Should I use this -> C++?

it's simply redundant to use this-> to call members, unless you want to semantically distinguish between locals and members quickly. a lot of people use the m_ prefix for class members, to avoid writing this-> all the time.

What is member function in C?

Member functions are operators and functions that are declared as members of a class. Member functions do not include operators and functions declared with the friend specifier. These are called friends of a class.

What does the this keyword do in C++?

In C++ programming, this is a keyword that refers to the current instance of the class. There can be 3 main usage of this keyword in C++. It can be used to pass current object as a parameter to another method. It can be used to refer current class instance variable. It can be used to declare indexers.

When a member function is defined inside the class then it is treated?

If a member function is defined inside a class declaration, it is treated as an inline function, and there is no need to qualify the function name with its class name.


1 Answers

I would leave it alone. This might have been a deliberate choice as an old-fashioned version of the SafeNavigationOperator. As you say, in new code, I wouldn't recommend it, but for existing code, I'd leave it alone. If you do end up modifying it, I'd make sure that all calls to it are well-covered by tests.

Edit to add: you could choose to remove it only in debug versions of your code via:

CThingy *CLookupThingy::Lookup( name )  { #if !defined(DEBUG)    if (!this)    {       return NULL;    } #endif    // else do the lookup code... } 

Thus, it wouldn't break anything on production code, while giving you a chance to test it in debug mode.

like image 142
Ben Hocking Avatar answered Oct 17 '22 21:10

Ben Hocking