views:

308

answers:

7

I have some code in Image.cpp:

Image::Image( int width, int height, int depth ) : m_sFileName(0)  
{  
...  
}  

and in Image.h:  
class Image: public DrawAble, public RenderAble  
{  
...  
private :  
    std::string *m_sFileName;  
};

My question is: what is happening with m_sFilename in the first line? I guess it is set to NULL but what's the point of doing it that way. Would it be the same to do:

Image::Image( int width, int height, int depth )  
{  
    m_sFileName(0);  
...  
}
A: 

It would be the same as doing

Image::Image( int width, int height, int depth )
{
    m_sFileName = 0;
    // ...
}

Please note that using a pointer to a std::string is usually not a good idea, as an empty string is an equally good nothing-here-marker and you do not have to care about the destruction if you make it a normal member.

Timbo
It's not exactly the same because if you do the latter, you can't create a const Image. Probably not something you want to do in this case, but potentially interesting for more lightweight types.
Martin B
If you've got a const image you'd probably want a const m_sFileName too, so I don't see the problem.
MSalters
A: 
m_sFileName(0)

in the constructor's body will be interpreted as calling a function with the name m_sFileName. You could replace it with

m_sFileName = 0;

However, the recommended initialization is in the initialization list of the constructor, like in the first example. Any data member that is not initialized in the initialization list of the constructor will be automatically initialized with the default constructor of its type.

Cătălin Pitiș
A: 

It does the same as:

Image::Image( int width, int height, int depth )  
{  
    m_sFileName = 0;
 ...  
}
Lucas
You guys are quick ...
Lucas
+9  A: 

The first uses what's called an initialization list.

When you enter the body of the constructor, all of the classes members must have been constructed (so they can be used). So if you have this:

class Foo
{
public:
    Foo()
    : str() // this is implicit
    {
        str = "String.";
    }
private:
    std::string str;
};

So, str gets constructed, then assigned. Better would have been:

class Foo
{
 public:
    Foo()
    : str("String.")
    {
    }
private:
    std::string str;
};

So that str gets directly constructed. This does not make a difference in your case because pointers have no constructor.

It is generally considered good practice to use an initialization list over running code in the constructor. The initialization list should be used for initializing, the constructor should be used for running code.

Also, why use a pointer to string? If you want a string, use a string; not a pointer to string. Chances are, you actually want a string.


More about initializer lists:

Initializer lists have more uses than just initializing members of the class. They can be used to pass arguments into base constructors:

class Foo
{
public:
    Foo(int i) { /* ... */ }
}

class Bar
    : public Foo
{
public:
    Bar()
    : Foo(2) // pass 2 into Foo's constructor.
             // There is no other way of doing this.
    {
        /* ... */
    }
};

Or constant members:

class Foo
{
public:
    Foo()
    : pi(3.1415f)
    {
        pi = 3.1415f; // will not work, pi is const.
    }
private:
    const float pi;
};

Or references:

class Foo
{
public:
    Foo(int& i)
    : intRef(i) // intRef refers to the i passed into this constructor
    {
        intRef = i; // does *not* set intRef to refer to i!
                    // rather, it sets i as the value of
                    // the int intRef refers to.
    }
private:
    int &intRef;
};
GMan
That's a good answer! Also want to notice that sometimes initialization lists are the only way to do it because const member variables and references can not be assigned...so in this case you have to use initialization lists.
Javier De Pedro
Heh, you typed the comment while I edited. :)
GMan
Great answer. With a lot more detail than I had hoped for! I guess the word missing in my vocabulary was "initialization lists". So now I know. Thankx!
Jacob Kristensen
Now it is a far more complete answer!
Javier De Pedro
+2  A: 

This is called an initializer. You should get used to using them. In this case it doesn't matter. But in other cases not using them could mean a double initialization of a non-pointer-member. First with the default values, then with your values. And finally there is the case of a member without a constructor without parameters. In those cases you have no choice but using an initializer.

h0b0
A: 

The syntax you are using:

Image::Image( int width, int height, int depth ) : m_sFileName(0)  
{  
...  
}

is called an initialization list. It will assign the value 0 to your member variable.

Using m_sFileName = 0; in the constructor body would be less performant because the member would be initialized twice (one time automatically because it is not included in the initialization list, and a second time with your explicit initialization).

Xavier Poinas
A: 

Those two variants are almost the same -- you're correct that the

: m_sFileName(0)

is causing m_sFileName to be initialized to 0.

The reason why C++ has this special initialization syntax becomes important when you want to create a const Image. (Probably not something you want to do in this case, but it can something you might want to do for "lightweight" types.) For a const Image, this is a const pointer in the constructor as well as in every "normal" member function, and so m_sFileName=0 is not allowed.

To solve this problem, C++ has initialization lists, which perform initialization, not assignment. Incidentally, if m_sFileName were an object, there would be an additional difference besides the const considerations: The initialization list would cause m_sFileName's constructor to be called, whereas the assignment would call the assignment operator.

Apart from all of these considerations, initialization lists are a good way to communicate intent -- to signify that you're initializing, not assigning.

Martin B