views:

883

answers:

14

I'm dealing with a large code base that uses the following construct throughout

class MyClass
{
public:
  void f(int x);
private:
  int x;
};


void MyClass::f(int x)
{
'
'
  this->x = x;
'
'
}

Personally, I'd always used and hence prefer the form

class MyClass
{
public:
  void f(int x);
private:
  int _x;
};


void MyClass::f(int x)
{
'
'
  _x = x;
'
'
}

The reasons I prefer the latter are that it is more succinct (less code = fewer potential bugs), and that I don't like having multiple variables of the same name in scope at the same time where I can avoid it. That said, I am seeing the former usage more and more often these days. Is there any upside to second approach that I am unaware of? (e.g. effect on compile time, use with templated code, etc...) Are the advantages of either approach significant enough merit a refactor to the other? Reason I ask, that while I don't like the second approach present in the code, the amount of effort and associated risk of introducing further bugs don't quite merit a refactor.

A: 

I agree. I don't like that naming convention - I prefer one where there is an obvious distinction between member variables and local variables. What happens if you leave off the this?

1800 INFORMATION
+2  A: 

I always use the m_ naming convention. Although I dislike "Hungarian notation" in general, I find it very useful to see very clearly if I'm working with class member data. Also, I found using 2 identical variable names in the same scope too error prone.

Dimitri C.
+1  A: 

class MyClass{
public:  
  int x;  
  void f(int xval);
};
//
void MyClass::f(int xval){  
  x = xval;
}
Alexey Malistov
What's the difference?
Groo
He hasn't named the function paramter the same as the member variable -- therefore not overloading the name and forcing this->
Pod
I do not think it brings any useful arguments to the discussion
Newtopian
A: 

In my opinion this tends to add clutter to the code, so I tend to use different variable names (depending on the convention, it might be an underscore, m*, whatever).

Dario Solera
+3  A: 

This usage of 'this' is encouraged by Microsoft C# coding standards. It gives a good code clarity, and is intended to be a standard over the usage of m_ or _ or anything else in member variables.

Honestly, I really dislike underscore in names anyway, I used to prefix all my members by a single 'm'.

Jem
Thanks for sharing your variable naming preference with us all.
Eric
C# coding standards :)To answer the question more explicitly, I would not consider it as a code smell, because it is even considered as good practice in another language where IMHO similar pros and cons apply.
Jem
@Eric it was to illustrate that it is a matter of taste. Underscores anywhere in non-standard library code tend to reduce the visibility, and could be considered as code smell, compared to the usage of this. This is of course just an opinion.
Jem
C# and C++ are different languages. In C++ this-> has a very specific use to force the lookup of an identifier in a template instantiation context as a member, rather than in the template definition context. A C++ coding standard might reasonably restrict the use of this-> to places where it is truly needed. This doesn't apply in C#.
Charles Bailey
I tend to use m_ on MFC code and a single underscore elsewhere, again just personal preference.
Shane MacLaughlin
+16  A: 

Your version is a bit cleaner, but while you're at it, I would:

  1. Avoid leading underscore: _x is ok until somebody chooses _MyField which is a reserved name.
  2. Make the attribute private or protected: the change is safe if it compiles, and you'll ensure your setter will be used.
  3. The this-> story has a use, for example in templated code to make the field name dependent on your type (can solve some lookup issues).

A small example of name resolutions which are fixed by using an explicit this-> (tested with g++ 3.4.3):

#include <iostream>
#include <ostream>

class A
{
public:
  int g_;
  A() : g_(1) {}
  const char* f() { return __FUNCTION__; }
};

const char* f() { return __FUNCTION__; }
int g_ = -1;

template < typename Base >
struct Derived : public Base
{
  void print_conflicts()
  {
    std::cout << f() << std::endl; // Calls ::f()
    std::cout << this->f() << std::endl; // Calls A::f()
    std::cout << g_ << std::endl; // Prints global g_
    std::cout << this->g_ << std::endl; // Prints A::g_
  }
};

int main(int argc, char* argv[])
{
   Derived< A >().print_conflicts();
   return EXIT_SUCCESS;
}
bltxd
Actually, the bulk of the code is actually MFC and ends up being m_ as per most MFC. I went with the simple underscore instead to make the question a bit more neutral, and similarly left out the private as it is a different issue. +1 for the point about templates, maybe you could supply a short example as it would really benefit the answer.
Shane MacLaughlin
Leading underscore is fine as long as the next character is lower case.
GMan
Correct, but the maintainer following you may not be aware of that.
bltxd
Thanks for the example
Shane MacLaughlin
+10  A: 

Field naming has nothing to do with a codesmell. As Neil said, field visibility is the only codesmell here.

There are various articles regarding naming conventions in C++:

etc.

Groo
I wasn't actually asking whether the naming convention was a code smell, I was asking whether excessive use of 'this->' was a code smell. I suspect Marco (below) is right in that the practice of adding the 'this->' comes from a desire to use Intellisense/Code completion in some IDEs. If this is the case than I believe it probably is a code smell.
Shane MacLaughlin
+2  A: 

A lot of people use this because in their IDE it will make a list of identifiers of the current class pop up.

I know I do in BCB.

I think the example you provide with the naming conflict is an exception. In Delphi though, style guidelines use a prefix (usually "a") for parameters to avoid exactly this.

Marco van de Voort
It's usually quicker to type Ctrl+Space or whatever the autocomplete shortcut for your IDE is; Visual Studio normally doesn't require this -> to see the methods and variables in scope.
Pete Kirkham
+1 for the most probable reason as to why I am seeing this so much in the code base. Interesting that the IDE is encouraging people to write code differently to how they would do it in a simpler editor.
Shane MacLaughlin
@Pete, agreed but I think Marco has probably given the reason why it is happening so much.
Shane MacLaughlin
Pete: BCB/Delphi do too, but this gives _only_ the identifiers in the class, not all in scope.
Marco van de Voort
A: 
Pete Kirkham
No you don't: Foo::Foo(int x) : x(x) { } // works as intended, the first and third x are the argument, the second x is this->x
MSalters
A: 
class MyClass
{
public:
  int m_x;
  void f(int p_x);
};


void MyClass::f(int p_x)
{
  m_x = p_x;
}

...is my preferred way using scope prefixes. m_ for member, p_ for parameter (some use a_ for argument instead), g_ for global and sometimes l_ for local if it helps readability.

If you have two variables that deserve the same name then this can help a lot and avoids having to make up some random variation on its meaning just to avoid redefinition. Or even worse, the dreaded 'x2, x3, x4, etc'...

Dan Brown
I have often seen p_ used to indicate a pointer in c++ which could be confusing here.
g .
A: 

If you have a problem with naming conventions you can try to use something like the folowing.

class tea
{
public:
    int cup;
    int spoon;
    tea(int cups, int spoons);
};

or

class tea
{
public:
    int cup;
    int spoon;
    tea(int drink, int sugar);
};

I think you got the idea. It's basically naming the variables different but "same" in the logical sense. I hope it helps.

Secko
I mistook the question for something else. Now I see that you really wanted to know another approach to the notation.
Secko
+1  A: 

Using 'this' in this manner is IMO not a code smell, but is simply a personal preference. It is therefore not as important as consistency with the rest of the code in the system. If this code is inconsistent you could change it to match the other code. If by changing it you will introduce inconsistency with the majority of the rest of the code, that is very bad and I would leave it alone.

You don't want to ever get into a position of playing code tennis where somebody changes something purely to make it look "nice" only for somebody else to come along later with different tastes who then changes it back.

markh44
A: 

I don't like using "this" because it's atavistic. If you're programming in good old C (remember C?), and you want to mimic some of the characteristics of OOP, you create a struct with several members (these are analogous to the properties of your object) and you create a set of functions which all take a pointer to that struct as their first argument (these are analogous to the methods of that object).

(I think this typedef syntax is correct but it's been a while...)

typedef struct _myclass
{
   int _x;
} MyClass;

void f(MyClass this, int x)
{
   this->_x = x;
}

In fact I believe older C++ compilers would actually compile your code to the above form and then just pass it to the C compiler. In other words -- C++ to some extent was just syntactic sugar. So I'm not sure why anyone would want to program in C++ and go back to explicitly using "this" in code -- maybe it's "syntactic Nutrisweet"

eeeeaaii
+3  A: 

My personal feeling is that fighting an existing coding convention is something you should not do. As Sutter/Alexandrescu puts it in their book 'C++ coding conventions': don't sweat the small stuff. Anyone is able to read the one or the other, whether there is a leading 'this->' or '_' or whatever.

However, consistency in naming conventions is something you typically do want, so sticking to one convention at some scope (at least file scope, ideally the entire code base, of course) is considered good practice. You mentioned that this style is used throughout a larger code base, so I think retrofitting another convention would be rather a bad idea.

If you, after all, find there is a good reason for changing it, don't do it manually. In the best case, your IDE supports these kind of 'refactorings'. Otherwise, write a script for changing it. Search & replace should be the last option. In any case, you should have a backup (source control) and some kind of automated test facility. Otherwise you won't have fun with it.