29

I have code like this:

class MapIndex
{
private:
    typedef std::map<std::string, MapIndex*> Container;
    Container mapM;

public:
    void add(std::list<std::string>& values)
    {
        if (values.empty()) // sanity check
            return;

        std::string s(*(values.begin()));
        values.erase(values.begin());
        if (values.empty())
            return;

        MapIndex *&mi = mapM[s];  // <- question about this line
        if (!mi)
            mi = new MapIndex();
        mi->add(values);
    }
}

The main concern I have is whether the mapM[s] expression would return reference to NULL pointer if new item is added to the map?

The SGI docs say this: data_type& operator[](const key_type& k) Returns a reference to the object that is associated with a particular key. If the map does not already contain such an object, operator[] inserts the default object data_type().

So, my question is whether the insertion of default object data_type() will create a NULL pointer, or it could create an invalid pointer pointing somewhere in the memory?

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
Milan Babuškov
  • 59,775
  • 49
  • 126
  • 179

5 Answers5

25

It'll create a NULL (0) pointer, which is an invalid pointer anyway :)

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • 6
    I don't mind it being invalid, but want it to be "safe". You can easily check 0 pointer and you can call "delete" on it. Any reference (URL) to read about this? – Milan Babuškov Jun 01 '09 at 22:11
  • I don't have a reference at hand. But I'm pretty sure a pointer constructor will initialize it to 0 (just like all integral types, like `int`, `short`, ...). – Mehrdad Afshari Jun 01 '09 at 22:21
  • 22
    C++ Standard, 8.5 paragraph 5: 'To default-initialize an object of type T means: otherwise (neither non-POD nor array), the object is zero-initialized.' Just a couple of lines above: 'To zero-initialize an object of type T means: if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;' In the same standard, 3.9, paragraph 10: 'Arithmetic types (3.9.1), enumeration types, pointer types, and pointer to member types (3.9.2),[...] are collectively called scalar types.' So yes, a pointer will be default initialized to 0. – David Rodríguez - dribeas Jun 01 '09 at 22:29
  • Oh, BTW, I check with the current standard at hand, but there are drafts around (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf) where you can check some issues. Chances are you won't hit a dark spot where the final standard differs much from the draft. The standard can be bought on-line for about $30 (pdf) – David Rodríguez - dribeas Jun 01 '09 at 22:33
  • This behavior won't change in C++0x, as there is simply too much code which relies on this behavior. – MSalters Jun 02 '09 at 10:59
  • It seems this has changed. At least as I interpret [dcl.init] 11.6.7. (7.1) and (7.2) are for class and array types respectively. `(7.3) — Otherwise, no initialization is performed.` Am I missing something? – WorldSEnder Oct 14 '17 at 05:56
  • That being said, `operator[]` is now specified via `try_emplace`, so for pointer types in maps, this makes no difference – WorldSEnder Oct 14 '17 at 06:03
  • 1
    A null pointer value is not an invalid pointer value; it’s just no dereferenceable. – Davis Herring Dec 11 '19 at 14:29
21

Yes it should be a zero (NULL) pointer as stl containers will default initialise objects when they aren't explicitly stored (ie accessing a non-existant key in a map as you are doing or resizing a vector to a larger size).

C++ Standard, 8.5 paragraph 5 states:

To default-initialize an object of type T means:

  • If T is a non-POD class type (clause class), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor)
  • If T is an array type, each element is default-initialized
  • Otherwise, the storage for the object iszero-initialized.

You should also note that default initialisation is different to simply ommiting the constructor. When you omit the constructor and simply declare a simple type you will get an indeterminate value.

int a; // not default constructed, will have random data 
int b = int(); // will be initialised to zero
Jamie Cook
  • 4,375
  • 3
  • 42
  • 53
  • I think the takeaway here is correct, but the terminology is not. "default initialize" has a specific meaning in the C++ standard, and that is how you get a indeterminate value. So, when you say "...stl containers will default initialise objects" I think what you really mean is "stl containers will default *construct* objects". This is a form of *value initialization* in the C++ standard's terminology. Assuming I got that all right. It's a mess (: – jwd May 10 '22 at 17:43
5

UPDATE: I completed my program and that very line I was asking about is causing it to crash sometimes, but at a later stage. The problem is that I'm creating a new object without changing the pointer stored in std::map. What is really needed is either reference or pointer to that pointer.

MapIndex *mi = mapM[s];  // <- question about this line
if (!mi)
    mi = new MapIndex();
mi->add(values);

should be changed to:

MapIndex* &mi = mapM[s];  // <- question about this line
if (!mi)
    mi = new MapIndex();
mi->add(values);

I'm surprised nobody noticed this.

Milan Babuškov
  • 59,775
  • 49
  • 126
  • 179
  • The test `if (!mi)` should come out the same( i.e. `mi` is 0) regardless whether `mi` is a copy of or a reference to the map entry. The difference is that the subsequent assignment *actually updates the map entry* in case of the reference (which is probably desired). – Peter - Reinstate Monica Dec 05 '18 at 14:52
  • The question was answerable without going into this, so it was (and this isn’t really an answer). – Davis Herring Dec 11 '19 at 14:32
3

The expression data_type() value-initializes an object. For a class type with a default constructor, it is invoked; if it doesn’t exist (or is defaulted), such as pointers, the object is zero-initialized.

So yes, you can rely on your map creating a NULL pointer.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
Bojan Resnik
  • 7,320
  • 28
  • 29
0

Not sure about the crash, but there's definitely a memory leak as this statement:

if (!mi) 
    mi = new MapIndex(); 

always returns true, because pointer mi is not a reference to to what mapM is holding for a particular value of s.

I would also avoid using regular pointers and use boost::shared_ptr or some other pointer that releases memory when destroyed. This allows you to call mapM.clear() or erase(), which should call destructors of keys and values stored in the map. Well, if the value is POD such as your pointer then no destructor is called therefor unless manually deleted, while iterating through a whole map will lead to memory leaks.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83