What is the default constructor for C++ pointer? - c++

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?

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

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

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.

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.

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.

Related

Is circumventing a class' constructor legal or does it result in undefined behaviour?

Consider following sample code:
class C
{
public:
int* x;
};
void f()
{
C* c = static_cast<C*>(malloc(sizeof(C)));
c->x = nullptr; // <-- here
}
If I had to live with the uninitialized memory for any reason (of course, if possible, I'd call new C() instead), I still could call the placement constructor. But if I omit this, as above, and initialize every member variable manually, does it result in undefined behaviour? I.e. is circumventing the constructor per se undefined behaviour or is it legal to replace calling it with some equivalent code outside the class?
(Came across this via another question on a completely different matter; asking for curiosity...)
It is legal now, and retroactively since C++98!
Indeed the C++ specification wording till C++20 was defining an object as (e.g. C++17 wording, [intro.object]):
The constructs in a C++ program create, destroy, refer to, access, and
manipulate objects. An object is created by a definition (6.1), by a
new-expression (8.5.2.4), when implicitly changing the active member
of a union (12.3), or when a temporary object is created (7.4, 15.2).
The possibility of creating an object using malloc allocation was not mentioned. Making it a de-facto undefined behavior.
It was then viewed as a problem, and this issue was addressed later by https://wg21.link/P0593R6 and accepted as a DR against all C++ versions since C++98 inclusive, then added into the C++20 spec, with the new wording:
[intro.object]
The constructs in a C++ program create, destroy, refer to, access, and manipulate objects. An object is created by a definition, by a new-expression, by an operation that implicitly creates objects (see below)...
...
Further, after implicitly creating objects within a specified region of
storage, some operations are described as producing a pointer to a
suitable created object. These operations select one of the
implicitly-created objects whose address is the address of the start
of the region of storage, and produce a pointer value that points to
that object, if that value would result in the program having defined
behavior. If no such pointer value would give the program defined
behavior, the behavior of the program is undefined. If multiple such
pointer values would give the program defined behavior, it is
unspecified which such pointer value is produced.
The example given in C++20 spec is:
#include <cstdlib>
struct X { int a, b; };
X *make_x() {
// The call to std​::​malloc implicitly creates an object of type X
// and its subobjects a and b, and returns a pointer to that X object
// (or an object that is pointer-interconvertible ([basic.compound]) with it),
// in order to give the subsequent class member access operations
// defined behavior.
X *p = (X*)std::malloc(sizeof(struct X));
p->a = 1;
p->b = 2;
return p;
}
There is no living C object, so pretending that there is one results in undefined behavior.
P0137R1, adopted at the committee's Oulu meeting, makes this clear by defining object as follows ([intro.object]/1):
An object is created by a definition ([basic.def]), by a new-expression ([expr.new]), when implicitly changing the active member of a union ([class.union]), or when a temporary object is created ([conv.rval], [class.temporary]).
reinterpret_cast<C*>(malloc(sizeof(C))) is none of these.
Also see this std-proposals thread, with a very similar example from Richard Smith (with a typo fixed):
struct TrivialThing { int a, b, c; };
TrivialThing *p = reinterpret_cast<TrivialThing*>(malloc(sizeof(TrivialThing)));
p->a = 0; // UB, no object of type TrivialThing here
The [basic.life]/1 quote applies only when an object is created in the first place. Note that "trivial" or "vacuous" (after the terminology change done by CWG1751) initialization, as that term is used in [basic.life]/1, is a property of an object, not a type, so "there is an object because its initialization is vacuous/trivial" is backwards.
I think the code is ok, as long as the type has a trivial constructor, as yours. Using the object cast from malloc without calling the placement new is just using the object before calling its constructor. From C++ standard 12.7 [class.dctor]:
For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior.
Since the exception proves the rule, referrint to a non-static member of an object with a trivial constructor before the constructor begins execution is not UB.
Further down in the same paragraphs there is this example:
extern X xobj;
int* p = &xobj.i;
X xobj;
This code is labelled as UB when X is non-trivial, but as not UB when X is trivial.
For the most part, circumventing the constructor generally results in undefined behavior.
There are some, arguably, corner cases for plain old data types, but you don't win anything avoiding them in the first place anyway, the constructor is trivial. Is the code as simple as presented?
[basic.life]/1
The lifetime of an object or reference is a runtime property of the object or reference. An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor. [ Note: initialization by a trivial copy/move constructor is non-vacuous initialization. — end note ] The lifetime of an object of type T begins when:
storage with the proper alignment and size for type T is obtained, and
if the object has non-vacuous initialization, its initialization is complete.
The lifetime of an object of type T ends when:
if T is a class type with a non-trivial destructor ([class.dtor]), the destructor call starts, or
the storage which the object occupies is reused or released.
Aside from code being harder to read and reason about, you will either not win anything, or land up with undefined behavior. Just use the constructor, it is idiomatic C++.
This particular code is fine, because C is a POD. As long as C is a POD, it can be initialized that way as well.
Your code is equivalent to this:
struct C
{
int *x;
};
C* c = (C*)malloc(sizeof(C));
c->x = NULL;
Does it not look like familiar? It is all good. There is no problem with this code.
While you can initialize all explicit members that way, you cannot initialize everything a class may contain:
references cannot be set outside an initializer list
vtable pointers cannot be manipulated by code at all
That is, the moment that you have a single virtual member, or virtual base class, or reference member, there is no way to correctly initialize your object except by calling its constructor.
I think it shouldn't be UB. You make your pointer point to some raw memory and are treating its data in a particular way, there's nothing bad here.
If the constructor of this class does something (initializes variables, etc), you'll end up with, again, a pointer to raw, uninitialized object, using which without knowing what the (default) constructor was supposed to be doing (and repeating its behavior) will be UB.

Order of destruction in unordered_map

I have an unordered_map of objects. Each object, in its destructor, browses the unordered map to find other objects, and then tweaks these other objects. This will fail if the other objects are zombie objects, but if the other objects are entirely removed from the unordered_map, there is no problem.
My questions:
does this work if I erase() an object, and its destructor tries to look for itself in the unordered map? Specifically, is the destructor called first, or is the object removed from the unordered_map first, or is there no guarantee?
does this work if the unordered_map is destroyed? Specifically, will the unordered_map be in a valid state as each individual destructor is called?
The lifetime of an object of type T ends when [...] if T is a class type with a non-trivial destructor (12.4), the destructor call starts [...]
[§ 3.8/1 N4431]
And, further down
The properties ascribed to objects throughout this International Standard apply for a given object only during its lifetime
[§ 3.8/3 N4431]
And finally
[...] after the lifetime of an object has ended [...] any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. [...] The program has undefined behavior if [...] the pointer is used to access a non-static data member or call a non-static member function of the object [...]
[§ 3.8/5 N4431]
So, since you must have some kind of reference (e.g. a pointer, or a real reference, which I'd count as pointer here, too) to the map, and its lifetime has ended, accessing a member function (to get an iterator for example) will - as far as I'd read this part of the standard - lead to undefined behaviour.
I was also looking at the part of the standard about unordered containers and containers in general, and couldn't find an exception to the above or any clue about the state during destruction.
So: Don't do this. Neither with unordered containers, nor with any other object.
BTW: What kind of tweaking makes any sense when you do it on objects that will be destructed moments thereafter?
I think i found a decent solution. You could possibly encapsulate an unordered_map in a class and use its distructor to raise a flag and detect it as an edge case in the destructor of the object that is typaram in the hash table. Like this:
template<typename K, typename V>
struct hash_table
{
unordered_map<K, V> map;
bool is_being_deleted = false;
~hash_table()
{
is_being_deleted = true;
}
};
struct PageRefrence
{
string str;
int page;
hash_table<string, PageRefrence>& refTable;
~PageRefrence()
{
if (refTable.is_being_deleted == true) // When the map is in the procces of deletion
return;
else
{ // Normal case
auto x = refTable.map.find(str);
cout << (*x).second.page;
}
}
};
int main()
{
hash_table<string, PageRefrence> refTable;
refTable.map.insert({ "HELP",{"HELP",42,refTable} });
}

inserting temporary std::shared_ptr into std::map, is it bad?

I'm designing a class for my application that implements a lot of standard shared pointers and usage of standard containers such as std::map and std::vector
It's very specific question to the problem so I just copied a piece of code
from my header for clarification purposes..
here is a snapshot of that declarations from the header:
struct Drag;
std::map<short, std::shared_ptr<Drag>> m_drag;
typedef sigc::signal<void, Drag&> signal_bet;
inline signal_bet signal_right_top();
and here is one of the functions that uses the above declarations and a temporary shared_ptr which is intended to be used not only in this function but until some late time. that means after the function returns a shared pointer should be still alive because it will be assigned at some point to another shared_ptr.
void Table::Field::on_signal_left_top(Drag& drag)
{
m_drag.insert(std::make_pair(drag.id, std::make_shared<Drag>(this))); // THIS!
auto iter = m_drag.find(drag.id);
*iter->second = drag;
iter->second->cx = 0 - iter->second->tx;
iter->second->cy = 0 - iter->second->ty;
invalidate_window();
}
the above function first insert a new shared_ptr and then assigns the values from one object into another,
What I need from your answer is to tell whether is it safe to insert temporary shared_ptr into the map and be sure that it will not be a dangling or what ever bad thing.
According to THIS website the above function is not considered safe because it would much better to write it like so:
void Table::Field::on_signal_left_top(Drag& drag)
{
std::shared_ptr pointer = std::make_shared<Drag>(this);
m_drag.insert(std::make_pair(drag.id, pointer));
auto iter = m_drag.find(drag.id);
*iter->second = drag;
// etc...
}
well one line more in the function.
is it really required to type it like that and why ?
There's no difference between the two functions in regard to the std::shared_ptr, because the std::make_pair function will create a copy of the temporary object before the temporary object is destructed. That copy will in turn be copied into the std::map, and will then itself be destructed, leaving you with a copy-of-a-copy in the map. But because the two other objects have been destructed, the reference count of the object in the map will still be one.
As for handling the return value from insert, it's very simple:
auto result = m_drag.insert(...);
if (!result.second)
{
std::cerr << "Could not insert value\n";
return;
}
auto iter = result.first;
...
The code in the example given is different from your example code, because it is using the new operator instead of std::make_shared. The key part of their advice is here:
Since function arguments are evaluated in unspecified order, it is possible for new int(2) to be evaluated first, g() second, and we may never get to the shared_ptr constructor if g throws an exception.
std::make_shared eliminates this problem - any dynamic memory allocated while constructing an object within std::make_shared will be de-allocated if anything throws. You won't need to worry about temporary std::shared_ptrs in this case.

C++: Pointer and then roound brackets - what does it mean?

I have to make google test for some function wrote by someone else, and I've got situation which I never have before. See pseudocode below:
typedef boost::shared_ptr<CSomeClass> CSomeClass_sh_ptr;
CSomeClass_sh_ptr getSomething(int A)
{
if(A>3)
{
return CSomeClass_sh_ptr();
}
CSomeClass_sh_ptr pointerToCSomeClass = otherPointerToCSomeClass;
return pointerToCSomeClass;
}
So my question is - what does
CSomeClass_sh_ptr()
or
boost::shared_ptr<CSomeClass>()
mean?
In your context, CSomeClass_sh_ptr() is a default constructed instance of CSomeClass_sh_ptr. In other words, a default constructed boost::shared_ptr<CSomeClass>.
Note that boost::shared_ptr<SomeClass> is not a pointer. It is a type that manages a pointer. When default constructed, its managed pointer is NULL or nullptr.
Your misconception is that you consider boost::share_ptr<CSomeClass> a pointer, while it is actually a class managing a pointer to CSomeClass, therefore boost::share_ptr<CSomeClass>() denotes a construction of object of that class.
The name of a type, followed by a (), means to create a temporary of that type, value initialized. In your case, as others have pointed out, the type is an instantiation of a class template, thus a class. To value initialize a class is to call its default constructor, if it has one, or to zero-initialize it, if it has no constructors. (boost::shared_ptr has a default constructor, so it gets called). If you actually had a pointer, to value initialize it would be to zero-initialize it, which would result in a null pointer. (The default constructor of boost::shared_ptr mimics this aspect of pointer behavior; its default constructor creates a shared pointer which behaves like a null pointer.)

Pointers not associated to NULL by default in Qt

I discovered something playing with QtCreator and bugs I was having in a destructor working with gcc: pointers are not equal to NULL by default; this needs to be added manually (to the constructor in my case). Is this right? How come? What's their default value?
Pointers, as every other primitive type, start living uninitialized, which means that they may have any value (actually, it's undefined behavior to read from any uninitialized variable).
On the other hand, the constructor for structs and classes is guaranteed to run on creation of a class instance, which means that, if it is written correctly, the class starts living in a valid state.
Unless a constructor in place, there are no default values
Looking at int * x; x has an undefined value
std::string is refers to a class with a contructor so with std::string x there is a defined value for x: "empty string constructor (default constructor) Constructs an empty string, with a length of zero characters."