i'm self-teaching c++ and i get how pointers work. but the doc i'm using is quite literal and the examples don't really go into why or when pointers would be used. a couple of real world examples would help me retain the knowledge.
You use pointers when you want your objects to exist longer than the current stack. You can also use them to avoid copying objects into containers.
// TODO: Remember to call DeleteObjects() when you're done here!!
std::vector<MyObject*> Objects;
void Test()
{
MyObject *const pObject = new MyObject();
Objects.push_back(pObject);
}
void DeleteObjects()
{
std::vector<MyObject*>::iterator it = Objects.begin(), itEnd = Objects.end();
for (; it != itEnd; ++it)
{
delete *it;
}
Objects.clear();
}
This is not an easy question to give a short and easy answer to, and I'm sure there's plenty of resources out there talking about pointers. Basically, whenever you'd like to use indirection (which may be even recursively) you need pointers.
Say for example a binary tree data structure, where each node have pointers to it's left and right sub trees, where either might a pointing to 0 (or NULL, meaning invalid pointer) to signify there's no sub tree there. This structure might look like this (not very C++-y, but that's a different story)
struct TreeNode
{
TreeNode* left;
TreeNode* right;
}
You can't use anything BUT a pointer in this case, as it'd be an infinitely large structure.
Uploading multiple data from a function. The caller would supply addresses of memory locations to be overwritten by the function
Dynamic memory allocation. Allocators would return pointers to newly allocated objects.
Passing array arguments: pass address instead of copying, to save performance for constant data.
This is not really a C++ matter, rather it is a C matter. For the beginner level, I would love to recommend the book Understanding Pointers in C
A simple example use of pointers is in linked lists. More info on wikipedia.
Pointers are useful when you need a function to return more than one variable. As an example, consider you are shopping at a grocery store. Each product has a name and a price. The name would be a string and the price a double. If there were a function called "buy", and you wanted to return both the name and price of the item, you might want to use a pointer.
void print_values(int* iptr, int size)
{
int i;
for (i=0; i < size; i++)
{
printf("%d ", *(iptr++));
}
printf("\n");
}
int main()
{
int table[256];
memset(table, 0, sizeof(table));
print_values(table, sizeof(table)/sizeof(int));
}
Or like a array of functions (example):
#define ___FUNC_B { func_1, func_2, func3 }
void ( __closure __fastcall * __FUNC_B [__FUNC_MAX] )( TObject* ) = ___FUNC_B;
Usage objects by pointers is in many cases is better:
CClass *ptr = new CClass();
/* something */
delete ptr;
If you have many objects and you must for example get it in a some arrange (eg. sort) you can use pointers for sort pointers to objects non objects:
vector <CClass*> Vptr;
for (i=0; i < 100; i++)
{
Vptr.push_back(new CClass());
}
sort(Vptr.begin(), Vptr.end(), __cmp_func);
/* something */
while (!Vptr.empty())
{
delete *(Vptr.begin());
Vptr.erase(Vptr.begin());
}
For dynamic memory alocation in C language:
char *memory = (char*) malloc(1024);
if (memory == NULL)
{
exit(1);
}
/* you have alocated 1KB in memory */
memory = (char*) realloc(2048);
if (memory == NULL)
{
exit(1);
}
/* you have alocated 2KB in memory */
free(memory);
/* you have nothing */
whats an example usage of c++
pointers?
Pointers address the following issues:
avoiding copying large chunks of memory around. That was in C at least, in C++ the preferred method is to use references. You can still use pointers if you want though.
allocating memory at runtime. This is needed when you have no idea at compilation how much memory you will use.
remembering the addresses of (member) functions for deferred calls (and callbacks).
allocating memory that outlasts it's current scope (it's still allocated after scope is finished).
sharing an object between multiple entities (multiple objects, multiple threads and so on). This means that you can pass the address of an object around and all entities using it will access the same data, not identical copies of it.
Sometimes pointers are also used as handles. That is, if you want to allow client code to uniquely identify a chunk of data without caring (or knowing) what the data is, you cast the address of the data (the pointer) to int/some other type and pass it around as a handle. This is commonly found in APIs that offer handles to client code but don't allow client code access to the real data (see use of WinAPI's HANDLE, HWND and so on - those are pointers in the internal implementation, but you don't know - or care - what the actual data is in order to use it).
Okay, I've seen so many terrible responses that I feel myself obligated to add yet another one.
First things first: we are talking C++ here. So many C uses are completely invalidated.
Terrible uses of pointers
You should learn RAII: this example is completely unsafe in the face of exception
// BAD
void func(size_t n)
{
int* array = new int[n];
// .. use array
delete[] array;
}
// GOOD
void func(size_t n)
{
std::vector<int> array(n, 0);
// .. use array
}
Rule of Thumb: if you see delete, you're doing it wrong. Chances are that if you see new too, though it's not as true because of argument forwarding issues.
Use references whenever possible
// BAD: Contract: do not pass NULL
void func(int* i);
// GOOD
void func(int& i);
Whenever the passing NULL does not make sense (and you don't need to rebind), use either a plain value or a (const) reference instead of a pointer.
Good uses of pointers:
Aliasing
void printSorted(std::vector<BigType> const& values)
{
std::vector<BigType*> references = from(values);
std::sort(references.begin(), references.end(), ByPointer());
std::transform(references.begin(), references.end(),
std::ostream_iterator<BigType>(std::cout, " "),
Dereference());
}
Optional Result
Object* find(Key const& key);
this is equivalent to:
boost::optional<Object&> find(Key const& key);
but quite less verbose.
Clone method
The use of a bare pointer as the return type of the clone method is mandated by the Boost Cloneable concept:
struct Base
{
virtual ~Base() {}
virtual Base* clone() const = 0;
};
There is a sound reason: taking advantage of covariant return types for the overloading of virtual methods. This allows us to write:
struct Derived: Base
{
virtual Derived* clone() const { return new Derived(*this); }
};
Thus taking full advantage, when cloning from a Derived const&, of the fact that we know that what is returned is at least a Derived.
It is up to the programmer to take care of the allocated memory unfortunately, so it must be used in conjunction with Smart Containers:
std::unique_ptr<Base> u = derived.clone();
A pointer can be considered a simple variable, but instead of saving a value it saves an adress to the position of the memory that stores a value.
Think the memory as a block of drawers and in each drawer you can put a value, to make it easier to find the values, you numerus the drawers. So, a position of memory would be a drawer and the block would be the full memory.
So, when you create a pointer, for example:
int* drawer = 0;
You are referring to the drawer which is labelled with the number 0 and contains an integer value, now you may think, ok, but, how can I get that value? Well, it's simple:
int value = *drawer;
By the same way you can store a new value on that drawer (memory address):
*drawer = 15;
Now comes the fun, the block of drawers is magic, and a drawer can take you to another drawer of the block, if we label them with the same number, the value that one stores is the same in the other:
int* drawer = 0;
int* drawer_copy = drawer;
*drawer = 15;
And what happens? That "drawer_copy", which is referring to the address 0 as "drawer" allows you to access to the integer value 15.
We can also save the address of a normal variable, we use the "&" prefix to get that adress:
int value = 15;
int* drawer = &value;
If we do this now:
value = 5;
"*drawer" will return a 5.
As you can see, pointers allow the user to have more control on the memory and to have reserved memory permanently, once you have declared a pointer you can preserve an address and access to it whenever you want. :)
Related
I would like to ask you how to reallocate a struct array in C++?
In C there is realloc which is quite good, but it is not recommended to use it in C++. Maybe some of you would tell me that I should not use a struct array?
Well, in this task we cannot use any STL containers, so struct is the only option, I suppose. It is for the matter of practice with allocation, reallocation of memory and other things...
In the example bellow I wrote a code how I would do it in C by using malloc and realloc. Can you give me an advice how to do it in C++.
Thanks.
class CCompany
{
public:
CCompany();
bool NewAccount(const char * accountID, int initialBalance);
struct ACCOUNT
{
char *accID;
int initialBalance;
...
};
ACCOUNT* accounts ;
...
...
private:
int ReallocationStep = 100;
int accountCounter = 1;
int allocatedAccounts = 100;
...
}
CCompany::CCompany()
{
accounts = (ACCOUNT*)malloc(allocatedItems*sizeof(*accounts));
}
bool CCompany::NewAccount(const char * accountID, int initialBalance)
{
// Firstly I check if there is already an account in the array of struct. If so, return false.
...
// Account is not there, lets check if there is enough memory allocated.
if (accountCounter == allocatedAccounts)
{
allocatedAccounts += ReallocationStep;
accounts = (ACCOUNT *) realloc(accounts, allocatedAccounts * sizeof(*accounts));
}
// Everything is okay, we can add it to the struct array
ACCOUNT account = makeStruct(accID, initialBalance);
accounts[CounterAccounts] = account;
return true;
}
If You have no possibility to use STL containers, maybe You should consider using some sort of list instead of array. Basing on Your code, this could be better solution than reallocating memory over and over the time.
Personally I don't think that realloc is not recommended in C++, yet for many uses of malloc, realloc, free there are other concepts in C++ (like new, placement new, delete, ...), shifting the semantics more on "objects" rather than on "plain memory".
So it is still valid to use the realloc-approach as you did; And - if dynamic data structures like linked lists are not a choice - actually the realloc-metaphor is the best I can think of, because it avoids unnecessarily copying, deleting, recreating items again and again while still providing a continuous block of memory holding all the objects.
According to other questions+answers(1, 2), you should avoid using malloc and realloc in C++ where possible.
The latter of those two references gives a good suggestion: If you're not allowed to use std::vector due to it being an STL container, perhaps std::fstream might be worth looking into as an alternative. This would suggest that working with files without relying upon excess working memory could be the intended goal of the assessment task. I can't see the assignment criteria, so I can't say whether or not this would be compliant.
Even with an assignment criteria on your side, some lecturers like to change requirements with little or no notice; in fact, sometimes just seeing a solution to the assignment that isn't what they had in mind will (unfairly) prompt such a modification. Any assessment that prompts you to reinvent std::vector seems silly to me, but if you have two options, and only one of them involves staying in your degree, I think your only solution will be to use realloc; there's no need for malloc here.
To reduce the overhead of calling realloc so often (as pointed out by another answer), you could remove two of your three counters, call realloc when the remaining counter is about to become a power of two, and reallocate by a factor of two like I did in push_back:
void *push_back(void **base, void const *value, size_t *nelem, size_t size) {
typedef unsigned char array[size];
array *b = *base;
if (SIZE_MAX / sizeof *b <= *nelem) {
return NULL;
}
if (*nelem & -~*nelem ? 0 : 1) {
b = realloc(b, (*nelem * 2 + 1) * sizeof *b);
if (!b) {
return NULL;
}
*base = b;
}
b += (*nelem)++;
return value
? memmove(b, value, sizeof *b)
: b;
}
The correct C++ way would be to use a std::vector which can deal nicely with reallocations. As your assignment do not allow you to use standard containers, you can:
either build a custom container using new and delete for reallocation and based on an array or a linked list
or directly use an array and stick to new and delete for reallocations - still acceptable C++
or revert to the good old malloc and realloc from the C standard library which is included in the C++ standard library. But you must be aware that this will not initialize the structs.
Because malloc/realloc would not call constructors, the last way must be seen as a low level optimization and the no initialization should be explicetely documented.
in Visual Studio 2010 i create a while statement in which i assign a pointer to pointer to a map.
Example:
std::map<int,std::tuple<int,std::string>** > dmap;
int i=0;
while (i<3){
std::tuple<int,std::string>* t = new std::tuple<int,std::string>(10+i,std::string("test"));
dmap[i] = &t;
dmap[i + 1 ] = &t;
i++;
}
.
.
.
for (auto it = d.begin();it!=d.end();++it)
{
if(*(it->second) != nullptr){
delete *(it->second);
*(it->second) = nullptr;
}
}
The problem is that the address of &t is always the same and so at the end the map always contains , for all keys that i entered, the last *t value.
What's the problem? (Resolved)
[Edit]
Now i modify the code beacause before it was incomplete, if i want to avoid to delete nullptr i need to have a pointer to pointer. Or not?
The problem is that you're putting a pointer to a local variable t into the map. After each loop, t is destroyed and the pointer is no longer valid.
I've no idea why you're using pointers at all, let alone pointers to pointers. You probably want to put the tuples themselves in the map:
std::map<int,std::tuple<int,std::string>> dmap;
for (int i = 0; i<3; ++i){
dmap[i] = {10+i, "test"};
}
i create a while statement in which i assign a pointer to pointer to a map
Sorry for saying this, but it sounds to me like you have bigger problems than the fact that t is the same (this looks like the xy problem).
Consider (in order) one of these alternatives:
store your tuples by value
store your tuples by single pointer (worse than "by value", better than "by pointer to pointer"). If you can do this, consider declaring your map over std::shared_ptr<std::tuple<...>>)
if you really need a map of pointers to pointers to tuples, consider creating a minimal proxy object that acts like a smart pointer to pointer internally (and manages the allocations for you in a safe manner) and like a regular type from the outside (and redeclare your map accordingly).
Either way, if you really need a map of pointers to pointers to tuples (for some reason), the allocation should be done like this:
std::map<int,std::tuple<int,std::string>**> dmap;
int i=0;
while (i<3) {
*dmap[ i ] = new std::tuple<int,std::string>{10 + i, "test"};
++i;
}
(The way you did it added the address of the same local (stack) variable to the map, which would lead to undefined behavior after you exit the local function).
Why are you interested in std::tuple<int,std::string>** ?
Wouldn't a std::tuple<int,std::string>* be sufficient ?
std::map<int,std::tuple<int,std::string>* > dmap;
int i=0;
while (i<3){
std::tuple<int,std::string>* t = new std::tuple<int,std::string>(10+i,std::string("test"));
dmap[i] = t;
i++;
}
Well, the address of t is always the same, because it is local variable that is stored on your stack. Each time you enter the block, t will be allocated on the same spot (as you're destroying t after you get out of your while body).
Instead, you need to allocate it on the heap (if this is really what you want to do).
std::tuple<int,std::string>** t = new std::tuple<int,std::string>*();
*t = new std::tuple<int,std::string>(10+i,std::string("test"));
dmap[i] = t;
I can't see what you're trying to accomplish, but this would be a better solution:
std::map<int,std::tuple<int,std::string>* > dmap;
int i=0;
while (i<3){
std::tuple<int,std::string>* t = new std::tuple<int,std::string>(10+i,std::string("test"));
dmap[i] = t;
i++;
}
Even better would be to use smart pointer instead raw one.
Even better would be to store objects by value (no pointers at all).
May I request that I any responder consider only "pure" C/C++ (whatever that means)? STL is ok. Boost is not.
I am writing my own C++ memory pool class (on a Linux system) for allocating and deallocating C++ objects in shared memory. I need this for access to the same objects over multiple processes. I will control access to memory pool object manipulation using POSIX semaphores, but I had a basic allocation/deallocation question. My code will only work for objects of the same size being allocated from the same pool. For the moment, we can ignore issues related to growth and shrinking of the pool dynamically.
Consider that I have a shared memory segment defined for a total of MAXFOOOBJECTS Foo objects. I define the shared memory segment in the following manner:
int shmid = shmget (somekey, ((sizeof(Foo) + 4) * MAXFOOOBJECTS) + 4, correctFlags);
void* sMem = shmat (shmid, (void*)0, 0);
By all the processes using this shared memory, the memory will be interpreted like so:
struct SharedMemStructure
{
int numberOfFooObjectsInPool;
Foo* ptrArray [MAXFOOOBJECTS]; // Pointers to all the objects in the array below
Foo objects [MAXFOOOBJECTS]; // Same as the value in the shmget call
};
Let us say I have an object Foo defined like so:
<Foo.h>
class Foo
{
public:
Foo ();
~Foo ();
void* operator new (); // Will allocate from shared memory
void operator delete (void* ptr); // Will deallocate from shared memory
private:
static void* sharedMem; // Set this up to be a POSIX shared memory that accesses
// the shared region in memory
static int shmid;
}
<Foo.cpp>
int Foo::shmid = shmget (somekey, ((sizeof(Foo) + 4) * MAXFOOOBJECTS) + 4, correctFlags);
void* Foo::sharedMem = shmat (shmid, (void*)0, 0);
void* Foo::operator new ()
{
void* thisObject = NULL;
sem_wait (sem); // Implementation of these is not shown
// Pick up the start of a chunk from sharedMem (will make sure this
// chunk has unused memory...
thisObject = (sharedMem + 4 + 4 * MAXFOOOBJECTS +
(sizeof (Foo) * sharedMem->numberOfFooObjectsInPool);
sharedMem->ptrArray[numberOfFooObjectsInPool] = thisObject;
sharedMem->numberOfFooObjectsInPool ++;
sem_post (sem);
return thisObject;
}
void Foo::operator delete (void* ptr)
{
int index = 0;
sem_wait (sem); // Implementation of these is not shown
// Swap the deleted item and the last item in the ptrArray;
index = (ptr - (sharedMem + 4 + (4*MAXFOOOBJECTS)))/(sizeof(Foo));
ptrArray[index] == ptrArray[numberOfFooObjectsInPool - 1];
numberOfFooObjectsInPool --;
sem_post (sem);
}
Now, my questions are:
Does the aforementioned scheme seem OK to you folks (O (1) for each of new and delete) or am I missing something utterly important? One problem I see immediately is that if the Foo object array is interpreted as a min heap, for example, I kill the heap property everytime I do the new and delete.
If I guarantee that this pool won't be used for min heap (say, as needed by Timer management techniques), do we have any issues with the aforementioned scheme?
On the flip side, I could manage the Foo array in the shared memory as a min or max heap (i.e., during new-ing and delete-ing) and incur O (lg n) worst case for each new or delete. Any comments?
Any other method that is preferable?
I find your idea ok, but your pointer arithmetic a bit cumbersome... and non portable also.
Generally speaking you should never access members of a struct adding the sizes of the previous members, as this is totally non portable (and quite ugly). Remember that the compiler may have alignment restrictions for the members of the struct, so it may insert padding bytes wherever it sees fit.
It is easier to use the struct SharedMemStructure that you presented:
int shmid = shmget (somekey, sizeof(SharedMemStructure), correctFlags);
SharedMemStructure* sharedMem = static_cast<SharedMemStructure*>(shmat (shmid, (void*)0, 0));
Then in the operator new:
//...
thisObject = &sharedMem[sharedMem->numberOfFooObjectsInPool];
//...
About your questions:
Of course, constant allocation complexity is incompatible with the heap properties. I think that O(log n) is the best you can get.
I see the scheme fine, but the details are in what these Foo class contains. As long as it does not have virtual functions, virtual base classes, pointers, references or any other C++ish construct you should be fine.
Yes, you can, why not?
If you don't need the heap property, a usual and easy optimization is to get rid of the ptrArray member and build a list of free slots using the first bytes of each free slot to point to the next free one.
Replace all the literal 4's with sizeof(int) and sizeof(Foo *) both for portability and readability. Or better yet, actually use the SharedMemStructure you have defined.
Scratch that, change the SharedMemStructure and then start using it. Your algorithm for tracking which slots are used is flawed. Once one item has been deleted, and the pointer list has been adjusted, there is no way to know which slots have been used without walking the entire list. A simple bool array would work, it will still require walking the list.
If you are really concerned about O(n), you need to maintain used and free linked lists. This can be done with a single fixed size array of int's.
size_t indices[MAXFOOOBJECTS];
size_t firstUsedIndex;
size_t firstFreeIndex;
Initialize firstUsedIndex to MAXFOOOBJECTS and firstFreeIndex to 0. indices gets initialized to 1 through MAXFOOOBJECTS. This way you can treat each entry in indices as a linked list where the contents is the "next" value and MAXFOOOBJECTS is your list terminator. Allocations can be done in constant time because you can grab the front node of the list. Deallocations will be linear since you have to find the node in the used list. You can find the node quickly with (ptr - poolStart) / sizeof(Foo) but you still need to find the previous node.
If you want to eliminate the reallocation costs as well, double the size of indices and treat it like a doubly linked list. The code is similar, but now you can do everything in constant time.
This looks like a problem:
int main() {
Foo* a = new Foo; // a == &sharedMem->objects[0]
Foo* b = new Foo; // b == &sharedMem->objects[1]
// sharedMem->ptrArray: {a, b, ...}
delete a;
// sharedMem->ptrArray: {b, ...}
Foo* c = new Foo; // c == &sharedMem->objects[1] == b!
}
I'm reading in values from a file which I will store in memory as I read them in. I've read on here that the correct way to handle memory location in C++ is to always use new/delete, but if I do:
DataType* foo = new DataType[sizeof(DataType) * numDataTypes];
Then that's going to call the default constructor for each instance created, and I don't want that. I was going to do this:
DataType* foo;
char* tempBuffer=new char[sizeof(DataType) * numDataTypes];
foo=(DataType*) tempBuffer;
But I figured that would be something poo-poo'd for some kind of type-unsafeness. So what should I do?
And in researching for this question now I've seen that some people are saying arrays are bad and vectors are good. I was trying to use arrays more because I thought I was being a bad boy by filling my programs with (what I thought were) slower vectors. What should I be using???
Use vectors!!! Since you know the number of elements, make sure that you reserve the memory first (by calling myVector.reserve(numObjects) before you then insert the elements.).
By doing this, you will not call the default constructors of your class.
So use
std::vector<DataType> myVector; // does not reserve anything
...
myVector.reserve(numObjects); // tells vector to reserve memory
You can use ::operator new to allocate an arbitrarily sized hunk of memory.
DataType* foo = static_cast<DataType*>(::operator new(sizeof(DataType) * numDataTypes));
The main advantage of using ::operator new over malloc here is that it throws on failure and will integrate with any new_handlers etc. You'll need to clean up the memory with ::operator delete
::operator delete(foo);
Regular new Something will of course invoke the constructor, that's the point of new after all.
It is one thing to avoid extra constructions (e.g. default constructor) or to defer them for performance reasons, it is another to skip any constructor altogether. I get the impression you have code like
DataType dt;
read(fd, &dt, sizeof(dt));
If you're doing that, you're already throwing type safety out the window anyway.
Why are you trying to accomplish by not invoking the constructor?
You can allocate memory with new char[], call the constructor you want for each element in the array, and then everything will be type-safe. Read What are uses of the C++ construct "placement new"?
That's how std::vector works underneath, since it allocates a little extra memory for efficiency, but doesn't construct any objects in the extra memory until they're actually needed.
You should be using a vector. It will allow you to construct its contents one-by-one (via push_back or the like), which sounds like what you're wanting to do.
I think you shouldn't care about efficiency using vector if you will not insert new elements anywhere but at the end of the vector (since elements of vector are stored in a contiguous memory block).
vector<DataType> dataTypeVec(numDataTypes);
And as you've been told, your first line there contains a bug (no need to multiply by sizeof).
Building on what others have said, if you ran this program while piping in a text file of integers that would fill the data field of the below class, like:
./allocate < ints.txt
Then you can do:
#include <vector>
#include <iostream>
using namespace std;
class MyDataType {
public:
int dataField;
};
int main() {
const int TO_RESERVE = 10;
vector<MyDataType> everything;
everything.reserve( TO_RESERVE );
MyDataType temp;
while( cin >> temp.dataField ) {
everything.push_back( temp );
}
for( unsigned i = 0; i < everything.size(); i++ ) {
cout << everything[i].dataField;
if( i < everything.size() - 1 ) {
cout << ", ";
}
}
}
Which, for me with a list of 4 integers, gives:
5, 6, 2, 6
I am observing strange behaviour of std::map::clear(). This method is supposed to call element's destructor when called, however memory is still accessible after call to clear().
For example:
struct A
{
~A() { x = 0; }
int x;
};
int main( void )
{
std::map< int, A * > my_map;
A *a = new A();
a->x = 5;
my_map.insert( std::make_pair< int, *A >( 0, a ) );
// addresses will be the same, will print 5
std::cout << a << " " << my_map[0] << " " << my_map[0]->x << std::endl;
my_map.clear();
// will be 0
std::cout << a->x << std::endl;
return 0;
}
The question is, why is variable a still accessible after its destructor was called by map::clear()? Do I need to write delete a; after calling my_map.clear() or is it safe to overwrite the contents of a?
Thanks in advance for your help,
sneg
If you store pointers on a map (or a list, or anything like that) YOU are the responsible for deleting the pointers, since the map doesn't know if they have been created with new, or not. The clear function only invokes destructors if you don't use pointers.
Oh, and one more thing: invoking a destructor (or even calling delete) doesn't mean the memory can't be accessed anymore. It only means that you will be accessing garbage if you do.
std::map does not manage the memory pointed to by the pointer values - it's up to you to do it yourself. If you don't want to use smart pointers, you can write a general purpose free & clear function like this:
template <typename M> void FreeClear( M & amap )
for ( typename M::iterator it = amap.begin(); it != amap.end(); ++it ) {
delete it->second;
}
amap.clear();
}
And use it:
std::map< int, A * > my_map;
// populate
FreeClear( my_map )
;
That's because map.clear() calls destructors of the data contained in the map, in your case, of the pointer to a. And this does nothing.
You might want to put some kind of smart pointer in the map for the memory occupied by a to be automatically reclaimed.
BTW, why do you put the template arguments in the call to make_pair? The template argument deduction should do pretty well here.
When you free a piece of heap memory, its contents don't get zeroed. They are merely available for allocation again. Of course you should consider the memory non accessible, because the effects of accessing unallocated memory are undefined.
Actually preventing access to a memory page happens on a lower level, and std libraries don't do that.
When you allocate memory with new, you need to delete it yourself, unless you use a smart pointer.
Any container stores your object Type and call corresponding constructors: internal code each node might look similar to:
__NodePtr
{
*next;
__Ty Val;
}
When you allocate it happens by constructing the val based on type and then linking. Something similar to:
_Ty _Val = _Ty();
_Myhead = _Buynode();
_Construct_n(_Count, _Val);
When you delete it calls corresponding destructors.
When you store references (pointers) it won't call any constructor nor it will destruct.
Having spent the last 2 months eating, sleeping, and breathing maps, I have a recommendation. Let the map allocate it's own data whenever possible. It's a lot cleaner, for exactly the kind of reasons you're highlighting here.
There are also some subtle advantages, like if you're copying data from a file or socket to the map's data, the data storage exists as soon as the node exists because when the map calls malloc() to allocate the node, it allocates memory for both the key and the data. (AKA map[key].first and map[key].second)
This allows you to use the assignment operator instead of memcpy(), and requires 1 less call to malloc() - the one you make.
IC_CDR CDR, *pThisCDRLeafData; // a large struct{}
while(1 == fread(CDR, sizeof(CDR), 1, fp)) {
if(feof(fp)) {
printf("\nfread() failure in %s at line %i", __FILE__, __LINE__);
}
cdrMap[CDR.iGUID] = CDR; // no need for a malloc() and memcpy() here
pThisCDRLeafData = &cdrMap[CDR.iGUID]; // pointer to tree node's data
A few caveats to be aware of are worth pointing out here.
do NOT call malloc() or new in the line of code that adds the tree node as your call to malloc() will return a pointer BEFORE the map's call to malloc() has allocated a place to hold the return from your malloc().
in Debug mode, expect to have similar problems when trying to free() your memory. Both of these seem like compiler problems to me, but at least in MSVC 2012, they exist and are a serious problem.
give some thought as to where to "anchor" your maps. IE: where they are declared. You don't want them going out of scope by mistake. main{} is always safe.
INT _tmain(INT argc, char* argv[]) {
IC_CDR CDR, *pThisCDRLeafData=NULL;
CDR_MAP cdrMap;
CUST_MAP custMap;
KCI_MAP kciMap;
I've had very good luck, and am very happy having a critical map allocate a structure as it's node data, and having that struct "anchor" a map. While anonymous structs have been abandoned by C++ (a horrible, horrible decision that MUST be reversed), maps that are the 1st struct member work just like anonymous structs. Very slick and clean with zero size-effects. Passing a pointer to the leaf-owned struct, or a copy of the struct by value in a function call, both work very nicely. Highly recommended.
you can trap the return values for .insert to determine if it found an existing node on that key, or created a new one. (see #12 for code) Using the subscript notation doesn't allow this. It might be better to settle on .insert and stick with it, especially because the [] notation doesn't work with multimaps. (it would make no sense to do so, as there isn't "a" key, but a series of keys with the same values in a multimap)
you can, and should, also trap returns for .erase and .empty() (YES, it's annoying that some of these things are functions, and need the () and some, like .erase, don't)
you can get both the key value and the data value for any map node using .first and .second, which all maps, by convention, use to return the key and data respectively
save yourself a HUGE amount of confusion and typing, and use typedefs for your maps, like so.
typedef map<ULLNG, IC_CDR> CDR_MAP;
typedef map<ULLNG, pIC_CDR> CALL_MAP;
typedef struct {
CALL_MAP callMap;
ULNG Knt;
DBL BurnRateSec;
DBL DeciCents;
ULLNG tThen;
DBL OldKCIKey;
} CUST_SUM, *pCUST_SUM;
typedef map<ULNG,CUST_SUM> CUST_MAP, CUST_MAP;
typedef map<DBL,pCUST_SUM> KCI_MAP;
pass references to maps using the typedef and & operator as in
ULNG DestroyCustomer_callMap(CUST_SUM Summary, CDR_MAP& cdrMap, KCI_MAP& kciMap)
use the "auto" variable type for iterators. The compiler will figure out from the type specified in the rest of the for() loop body what kind of map typedef to use. It's so clean it's almost magic!
for(auto itr = Summary.callMap.begin(); itr!= Summary.callMap.end(); ++itr) {
define some manifest constants to make the return from .erase and .empty() more meaningfull.
if(ERASE_SUCCESSFUL == cdrMap.erase (itr->second->iGUID)) {
given that "smart pointers" are really just keeping a reference count, remember you can always keep your own reference count, an probably in a cleaner, and more obvious way. Combining this with #5 and #10 above, you can write some nice clean code like this.
#define Pear(x,y) std::make_pair(x,y) // some macro magic
auto res = pSumStruct->callMap.insert(Pear(pCDR->iGUID,pCDR));
if ( ! res.second ) {
pCDR->RefKnt=2;
} else {
pCDR->RefKnt=1;
pSumStruct->Knt += 1;
}
using a pointer to hang onto a map node which allocates everything for itself, IE: no user pointers pointing to user malloc()ed objects, works well, is potentially more efficient, and and be used to mutate a node's data without side-effects in my experience.
on the same theme, such a pointer can be used very effectively to preserve the state of a node, as in pThisCDRLeafData above. Passing this to a function that mutates/changes that particular node's data is cleaner than passing a reference to the map and the key needed to get back to the node pThisCDRLeafData is pointing to.
iterators are not magic. They are expensive and slow, as you are navigating the map to get values. For a map holding a million values, you can read a node based on a key at about 20 million per second. With iterators it's probably ~ 1000 times as slow.
I think that about covers it for now. Will update if any of this changes or there's additional insights to share. I am especially enjoying using the STL with C code. IE: not a class in sight anywhere. They just don't make sense in the context I'm working in, and it's not an issue. Good luck.