Solved

Should CArray be used for a list of pointers in C++?

Posted on 2016-07-24
19
88 Views
Last Modified: 2016-07-29
I would like to create a variable size list of pointers two objects called myObject

Is this the correct format?

CArray <*myObject> pMyobjectPointers or is it this  CArray <myObject> *pMyobjectPointers?

and, how do I pass this CArray of pointers to a function?
0
Comment
Question by:cschene
  • 8
  • 4
  • 4
  • +2
19 Comments
 
LVL 24

Accepted Solution

by:
chaau earned 500 total points
Comment Utility
CArray can be used for the array of pointers. I specifically avoid using the term list, as the list is usually a slightly different collection in the programming word. The CArray is a vector type of collection.

That was a side note. If you want to know the programming basics read about lists vs arrays here.

Now, to your question. The correct syntax will be:

CArray<myObject*> myObjectPointers;
for(int i=0;i<10;++i) {
  myObject* pObject = new myObject;
  pObject->id=i;
  myObjectPointers.Add(pObject);
}
// do something with the array
for(int i=0; i<myObjectPointers.GetCount();++i){
  TRACE("the id is %d", myObjectPointers[i]->id;)
}

// clear the array. Note RemoveAll() is not enough:
for(int i=0; i<myObjectPointers.GetCount();++i){
  delete myObjectPointers[i];
}
myObjectPointers.RemoveAll()

Open in new window

}

Now, if you want to pass the whole array to another function use reference:
void MyFunction(CArray<myObject*> &myObjects)
{
// do something with the array
for(int i=0; i<myObjects.GetCount();++i){
  TRACE("the id is %d", myObjects[i]->id;)
}
}

Open in new window

If you need the array the data to a C-style function use GetData() method:
// the c function
void CStyleFunction(myObject** pData, int size)
{
for(int i = 0; i<size;++i){
  TRACE("the id is %d", pData[i]->id;)
}
}

//calling the c function:
CstyleFunction(myObjectPointers.GetData(), myObjectPointers.GetCount());

Open in new window

0
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
Just curious why you would want to use a non-portable type like Carray when you can use std::vector or std::array (the latter if you have a later C++ compiler).
1
 

Author Comment

by:cschene
Comment Utility
is std::array the same for all C++ compilers? even GNU? or the ones that run on Linux?
0
 
LVL 24

Expert Comment

by:chaau
Comment Utility
I would use std::vector to be on a safe side
1
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
>> is std::array the same for all C++ compilers?
I did have a caveat that it was available only on later compilers.
The compiler needs to support C++11.

std::vector will run consistently on C++03 or later.

>> even GNU? or the ones that run on Linux?
GNU compilers do run on Linux.

Here's an actual run I did to illustrate (on Cygwin):
$  g++ --version
  g++ (GCC) 4.9.2

$  g++ -std=c++11 std_array.cpp
$  ./a
Number Elements = 3
Size of Array = 12
First 2 values are: 3,4
#include <iostream>
#include <array>

int main()
{
    std::array<int,3> myStdArray = {3, 4,2};

    std::cout << "Number Elements = " << myStdArray.size() << std::endl;
    std::cout << "Size of Array = " << sizeof(myStdArray) << std::endl;
    std::cout << "First 2 values are: " << myStdArray[0] << "," << myStdArray[1] << std::endl;

    return 0;
}

Open in new window

1
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
If you know that the array size is fixed in length, and if you are confident that all your development platforms support C++11, then std::array is a good choice. If you think that you may need to resize the array, then std::vector or Carray is a good choice.

I was just curious why you want to use a non-portable container.
1
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
If you actually do plan on using C++11 across the board, then I would have to ask why you would want an array of pointers rather than using an array of smart pointers which are available in C++11. But, if interested, then that would surely be another set of questions as there are different kinds of smart pointers.
1
 
LVL 28

Expert Comment

by:pepr
Comment Utility
Partly off-topic but actually related: Have a look at the "C++ Core Guidelines" which is rather new initiative by Stroustrup and the other people around the standardization. Basically, it is the search for how the C++ code should be written. It is also the activity that leads to building tools for checking the rules and giving you suggestions.

You will find there also the things related to containers and pointers.

The GSL library was designed (the kind of related things that are expected to be developed more rapidly than the standard to support the guidelines.

https://isocpp.org/blog/2015/09/bjarne-stroustrup-announces-cpp-core-guidelines

You can read it directly on GitHub (as it is written in markdown and hence formated by GitHub)

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md
1
 
LVL 32

Expert Comment

by:sarabande
Comment Utility
Is this the correct format?
CArray <*myObject> pMyobjectPointers or is it this  CArray <myObject> *pMyobjectPointers?
and, how do I pass this CArray of pointers to a function?

to add to above comments:

if using MFC and CString you also can use CArray. there is an advantage of std::vector over CArray but if it was the only container you take from STL and if you don't have experiesnces with other containers of STL, it would not make so much sense to using stl classes where you have equivalent MFC classes. the same applies for C++11: if you don't know for sure that you alway have a c++11 compiler or have to maintain or use older code, i would use the new compiler but would avoid to become dependent on the newest features.

a few comments to your code:

you always should consider to use an array of objects instead of pointers.

pointers only should be used if you have multiple containers refering to the same objects. for example, if you have an array for all objects and two dictionaries (maps) where you can search for name or ID. for this, it is better to use pointers both in the array and the maps. however, all three containers would build a new super-container (a new class) and you should provide all functions necessary for insert, update, delete, and find.

another case where pointers must be used is for an array of function pointers or for an array of baseclass pointers. for both case, you have to use pointers.

you also should avoid pointers of the Array. in c++ you can pass it by reference. so there is no Need to have a pointer.

alltogether would end with code like:

std::vector<myObject> myObjects; 
myObjects.push_back(myObject(1, "A"));

// alternatively push an empty object and fill it later
myObjects.push_back(myObject());
myObject & mo = myObjects.back().;
mo.ID = 123;
mo.name = "B";

//and, how do I pass this array to a function?
bool b = myFunction(myObjects);

Open in new window


and myFunction would have prototype
bool MyClass::myFunction(std::vector<myObject> & objects);

Open in new window


Sara
1
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:cschene
Comment Utility
Hi Sara,

The reason I did not use an array of objects was that the variable was going out of scope and I would have required a very complex copy constructor since "myObject" contains a hierarchy of somewhat complex objects.

But in hindsight: maybe a copy constructor might make sense but it would not be a simple one.
0
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
In my high performance computing environment we had to dynamically create large objects. We could not afford the pushback operation because we would not be able to meet our timing goals. Since we were not using c++11, we used boost smart pointers in our containers.
0
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
Btw, one n nice thing about using shared smart pointers in a container in a multithreaded program is that one thread could delete the pointer with proper synchronisation, of course, and only after the last reference to the object was removed would the smart pointer invoke the object's destructor.
0
 
LVL 32

Expert Comment

by:sarabande
Comment Utility
 that the variable was going out of scope

Open in new window


variables stored in a member array  will not go out of scope. as you have seen in my code, you may have a default constructor that constructs an "empty" object and stores it in the container. in a second step you can retrieve a reference of the object from the container and "fill" it by using appropriate functions.

as long as you don't want to store objects of different derived classes into one container, you don't have disadvantages if you store objects.

smart pointers are much better than pointers since you don't have to care for deletion but the 'last' one which has access by pointer automatically would care for deletion. nevertheless smart pointers have the problem that they are not thread-safe (and it really is hard to make them thread-safe) and that pointers principially make your code less object-oriented since they mislead the programmer to do something with pointer rather than that the object the pointer points to would decide itself what must be done. generally it is a cleaner and simpler concept to have only one variable for each real object and store it by value into one container. for example if you have a list of addresses for mail it is a good concept to create that list only once and a class which cares for the list  and not use copies of the list from different branches in your program.

we had to dynamically create large objects. We could not afford the pushback operation
push_back is not expensive, if you create small default objects and expand these objects after they were already stored in the all container. of course, latest when multiple derived objects came into play, you need to use pointers. but, if you never use copies of the pointers but always use a reference of the pointer from container, you are safe and don't need smart pointers. you even can return references to object, then:

template <class T>
class MyContainer
{
     std::vector<T*> arr;
public:
     void push_back(const T & t) { arr.push_back(new T(t)); }
     T & operator[] (size_t i) { assert (i >= 0 && i < arr.size()); return *arr[i]; }
     ~MyContainer()  { while (!arr.empty()) { delete *arr.begin(); arr.erase(arr.begin()); } 
};

Open in new window


if you combine the above with a factory, you could read results from a database query directly into the container thus representing the full resultset of your query as an array of objects.

you will see that consequently using objects rather than pointers makes your programmer's life happier. if you have to use pointers encapsulate them into safe objects, at least to smart pointers.

finally, a pointer to a single container never makes sense.

Sara
0
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
pushback is not expensive if your object is a few ints - agree.
smart pointers are not thread safe - agree - never said they were.
pointers are not thread safe - also agree.
objects are not usually thread safe - also agree.

>> and it really is hard to make them thread-safe
- not when you thoroughly test a wrapper thread safe class and just use that pattern from now on (as was done on my previous project; and in the lab, we never had the problem you are speaking of.
- thread-safe is a difficult topic. Thanks for bringing it up in this thread as a warning to the wise.
0
 
LVL 32

Expert Comment

by:sarabande
Comment Utility
phoffric, there are a lot of good reasons to using pointers when you exactly know what you were doing. i am currently developing a complex class which uses pointers.

nevertheless in my opinion is the speed argument to favor pointers over objects weak. it is a fact that  for a std::vector which is storing objects you need two objects when using push_back, one to be passed to push_back as an argument and another to be created in the vector. but obviously you could always (beside of the very first time) use an existing or static object to pass it to push_back and have a 2-phase creation for your objects as shown above. also std::vector::reserve would milden or even compensate the negative effects of growing arrays with big elements.

so i think, if using std::vector for local use rather than for to implement your own container, you always should check whether you could store objects since it makes many things easier. also multi-threading doesn't necessarily need 'a wrapper-thread-save class'  if you can make sure that no pointers to elements of the vector are used somewhere else. you can fill the vector pass it to the thread by reference and don't access the vector until the thread has finished and signaled that the vector no longer was needed.

Sara
0
 

Author Comment

by:cschene
Comment Utility
Thanks all for the help and your great comments
0
 
LVL 32

Expert Comment

by:phoffric
Comment Utility
You're welcome. Hope we were able give you something to think about for the future.
1
 

Author Comment

by:cschene
Comment Utility
The discussion of the different approaches was interesting and educational. Thanks so much.

Next time I will consider a different approach.

My customer was time focused on getting the release out and since I had already implemented a fix using CArray  <*myobject> was anxious to deploy that solution.

 Next time I will consider a better approach.  I also considered using a copy constructor but the  object "myObject" is actually very complex and contains within it a number of other complex class objects that have many references and pointers: the copy constructor would have been very complex I believe.
0
 
LVL 32

Expert Comment

by:sarabande
Comment Utility
is actually very complex and contains within it a number of other complex class objects that have many references and pointers

the complexity of the big class is irrelevant for the copy constructor. if the class has no pointer members, you don't need to add a copy constructor at all since the default copy constructor already would do. so, you only have to care for classes with pointer members. here you need to add a copy constructor which makes a copy of of all members and for the pointers a deep copy by creating  a new pointer. if it is a class pointer you would use the copy constructor of the class. if not, you would do a memcpy of the data. actually, the job might be lengthy depending on the amount of members but is not difficult even for complex relations. it will not take more than a few hours and you can decide to use the copy constructors only for new code and let the old code as it is as long as it is working.

Sara
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Here is a helpful source code for C++ Builder programmers that allows you to manage and manipulate HTML content from C++ code, while also handling HTML events like onclick, onmouseover, ... Some objects defined and used in this source include: …
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
THe viewer will learn how to use NetBeans IDE 8.0 for Windows to perform CRUD operations on a MySql database.
The viewer will learn how to use and create keystrokes in Netbeans IDE 8.0 for Windows.

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now