Using array of pointers as parameter to method - c++

I have to use an array of pointers to Objects and I must also pass it as parameter to methods. However the way to do this eludes me. Here is the method I use for the initialization of the elements of the array. When I dereference them in main, their data are not correct (they contain memory addresses). What is the correct way? Might it be false the way I dereference them?
void createSquares(Square* squareArray[]){
PropertySquare PropertySquare1(1,"purple",120);
PropertySquare PropertySquare2(2,"purple",170);
squareArray[1] = &PropertySquare1;
squareArray[2] = &PropertySquare2;
.
.
.
}
In main:
Square *allSquares[22] ;
createSquares(allSquares);
cout<<"ID is: "<<allSquares[1]->getID()<<endl;
cin.get();
As I said the ID is finally a memory address.
Update based on answers:
I have tried this and it does not work as well.It is imperative for me to use polymorphism.
vector<Square*> allSquares;
createSquares(allSquares);
void createSquares(vector<Square*> &squares){
PropertySquare PropertySquare1(1,"purple",120);
PropertySquare PropertySquare2(2,"purple",170);
squares.push_back(&PropertySquare1);
squares.push_back(&PropertySquare2);
}
in main:
for (vector<Square*>::iterator it=allSquares.begin(); it!=allSquares.end();it++){
it->
}
It does not allow me to use the virtual functions of Square since it is abstract.
Any suggestion?

Everything you're doing is Not Good. It's tricky to figure out where to begin, so let me start at the end and present The Right Way:
typedef std::unique_ptr<Square> square_ptr;
void createSquares(std::vector<square_ptr> & v)
{
v.emplace_back(new PropertySquare1(1,"purple",120));
v.emplace_back(new PropertySquare1(2,"green",370));
// ...
}
int main()
{
std::vector<square_ptr> allSquares;
createSquares(allSquares);
for (const auto & p : allSquares)
std::cout << "ID is: " << p->getID() << std::endl;
}
Now to break down your problems:
First off, you are storing the pointers of local variables. Those local variables die at the end of the function scope, and the pointers become dangling. Dereferencing is a program error.
Second, to fix this, you should create dynamic objects: squareArray[1] = new PropertySquare1(1,"purple",120); However, that is problematic, too. Someone will have to clean up those objects! You could iterate over the array and call delete on each element.
Third, 22 is a "magic number" (because it's neither 0 nor 1). This should not be hard-coded. If the number really is a compile-time constant, name it somewhere.
Fourth, either way, don't use raw arrays. Either use a std::array if the size is known at compile-time, or a std::vector if the size is determined at runtime.
Fifth, putting it all together, a dynamic container of smart pointers takes care of all your worries. That's the one presented in my code. The alternative, a static array of smart pointers, wouldn't use an initialization function at all, but rather it'd be initialized right on the spot:
const std::size_t nfuncs = 22;
std::array<square_ptr, nfuncs> allSquares {
new PropertySquare1(1,"purple",120),
new PropertySquare1(2,"green",370),
// ...
};

What is false is that
PropertySquare PropertySquare1(1,"purple",120);
PropertySquare PropertySquare2(2,"purple",170);
Are destroyed the moment createSquares returns. So your array contains junk when you print those IDs.

Because PropertySquare variables are declared in stack, they will be destroyed once the function returns. You want to declare them in heap instead by
squareArray[1] = new PropertySquare PropertySquare1(1,"purple",120);
squareArray[2] = new PropertySquare PropertySquare1(2,"purple",170);
and delete them in main by calling delete allSquares[1] etc.

Related

Variable as reference not staying

So i read this thread and many others:
Function does not change passed pointer C++
Yet i still can't solve my issue.
I have a function declared like this:
void test(list<int*> *listNodes){
int v=5;
(*listNodes).push_back(&v);
(*listNodes).push_back(&v);
(*listNodes).push_back(&v);
for(int a = 0; a < (*listNodes).size(); a ++){
std::list<int*>::iterator i = (*listNodes).begin();
advance(i, a);
int *totry = *i;
cout << *totry;
cout << ",";
}
}
Wich works, and prints fine, by the i mean: the listNodes variable has 3 elements, all 5's. However, when this functions returns, the values are not updated. By that, i mean that the variable has trash. I call this function in another one like this:
void create(list<int*> listNodes){
test(&listNodes);
for(list<int*>::const_iterator it=listNodes.begin();
it!=listNodes.end(); it++){
int *show=*it;
cout << *show << '\n';
}
}
Again, in this function, the cout will output memory garbage instead of outputting the 3 fives.
Any ideas on how should i proceed to, when the function test comes back, i have the list populated?
The problem I believe you're thinking about (as opposed to other problems in this code) is not actually what you're thinking. The list DOES maintain its values, the problem is that the values it has are pointing to garbage memory.
When you do this:
int v=5;
(*listNodes).push_back(&v);
(*listNodes).push_back(&v);
(*listNodes).push_back(&v);
You are putting three copies of the address of v into the list. You have declared v as a stack variable that only exists for the duration of this function. When you print the values pointed to by the elements of listNodes inside function test, that variable still exists in that memory location.
When you later print out the values pointed to by the elements of listNodes in function create, that variable has gone out of scope and has been used by something else, hence the garbage.
Here are two possible solutions to consider:
Use list<int> instead of list<int *>. If all you want to do is store a list of integers, this is the way to go.
If, on the other hand, you really need to store pointers to those integers, you'll need to allocate memory off the heap:
int* v = new int(); // allocate an int on the heap
*v = 5; // store 5 in that int
(*listNodes).push_back(v); // save the pointer to the allocated
// memory in *listNodes
etc
This is not very good in terms of modern c++, however, as you generally don't want to be handling raw pointers at all, but it illustrates the point I think you are struggling with.
In this code,
void create(list<int*> listNodes){
listNodes=teste(&listNodes);
… the formal argument listNodes is passed by value. That means that the function receives a copy of whatever was passed as actual argument in a call siste. Changes to this copy will not be reflected in the actual argument.
The call to teste won't call the test function, since it's a different name.
In a way that's good, because test is declared as a void function so it can't return anything.
But it's also bad, because it means that a very crucial piece of your code, the teste function that's actually called, isn't shown at all in your question.
The test function,
void test(list<int*> *listNodes){
int v=5;
(*listNodes).push_back(&v);
for(int a = 0; a < (*listNodes).size(); a ++){
std::list<int*>::iterator i = (*listNodes).begin();
advance(i, a);
int *totry = *i;
cout << *totry;
cout << ",";
}
printf("\n");
}
… has a lot wrong with it.
Starting at the top, in C++ the pointer argument
void test(list<int*> *listNodes){
… should better be a pass-by-reference argument. A pointer can be null. That doesn't make sense for this function, and the code is not prepared to handle that.
Next, in
int v=5;
(*listNodes).push_back(&v);
… the address of a local variable is pushed on a list that's returned. But at that point the local variable ceases to exist, and you have a dangling pointer, one that used to point to something, but doesn't anymore. If the caller uses that pointer then you have Undefined Behavior.
Next, this loop,
for(int a = 0; a < (*listNodes).size(); a ++){
std::list<int*>::iterator i = (*listNodes).begin();
advance(i, a);
… will work, but it needlessly has O(n2) complexity, i.e. execution time.
Just iterate with the iterator. That's what iterators are for. Iterating.
Summing up, the garbage you see is due to the undefined behavior.
Just, don't do that.

Pointers to pointers in hash Tables

i don't quite understand why the following code does what it does.
void initializeTable()
{
NodeT* (*hashTable) ;
*hashTable=(NodeT*)malloc(30*sizeof(NodeT));
int i;
for(i=0;i<30;i++)
{
(*hashTable)[i].info=(char*)malloc(10*sizeof(char));
strcpy((*hashTable)[i].info,"a");
cout<<(*hashTable)[i].info<<" ";
}
}
I am trying to understand how hash Tables work. In the first version of my program I used something like NodeT* hashTable[arraySize] in order to create an array of pointers to NodeT, in order to do the chaining. After this I decided to try and allocate dinamycally memory for the array, so I tried to use the (I think) equivalent NodeT* (*mockTable).
My question is the following: why do I have to use (*hashTable)[i].info, and not (*hashTable)[i]->info ? As far as i concerned, hashTable is a pointer which points to an array of pointers to NodeT, so this shouldn't happen. What am i getting wrong here?
You got the basic idea but it seem you miscount the pointers
your hashTable is a pointer to pointer (**), and when you access it in the strcpy you actually get into the value of this double pointer the 1'st is with the array index [i] and the second is with the *hashTable which is when accessing a variable calling the Value inside it.
so if you define a duel pointer variable
StrutT **temp;
and you want to access a field inside it
you can
*temp[i].field
but in case you would only want to access at level of a single pointer (same **temp) you would need:
temp[i]->field
or
*temp->field
which both only "pop" the top most pointer

Create pointer to pointer in while statement issue

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).

whats an example usage of c++ pointers?

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. :)

Why is memory still accessible after std::map::clear() is called?

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.