Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

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

Posted on 2016-07-24
19
117 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:Christopher Schene
  • 8
  • 4
  • 4
  • +2
19 Comments
 
LVL 24

Accepted Solution

by:
chaau earned 500 total points
ID: 41726904
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
ID: 41726954
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:Christopher Schene
ID: 41726987
is std::array the same for all C++ compilers? even GNU? or the ones that run on Linux?
0
Networking for the Cloud Era

Join Microsoft and Riverbed for a discussion and demonstration of enhancements to SteelConnect:
-One-click orchestration and cloud connectivity in Azure environments
-Tight integration of SD-WAN and WAN optimization capabilities
-Scalability and resiliency equal to a data center

 
LVL 24

Expert Comment

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

Expert Comment

by:phoffric
ID: 41727002
>> 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
ID: 41727003
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
ID: 41727016
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 29

Expert Comment

by:pepr
ID: 41727130
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 33

Expert Comment

by:sarabande
ID: 41727776
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
 

Author Comment

by:Christopher Schene
ID: 41727785
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
ID: 41727912
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
ID: 41727927
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 33

Expert Comment

by:sarabande
ID: 41728148
 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
ID: 41728773
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 33

Expert Comment

by:sarabande
ID: 41728998
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:Christopher Schene
ID: 41733646
Thanks all for the help and your great comments
0
 
LVL 32

Expert Comment

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

Author Comment

by:Christopher Schene
ID: 41734162
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 33

Expert Comment

by:sarabande
ID: 41734239
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

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
How can i compile this github project?? 2 90
Visual Studio: built-in keystroke automation 2 71
Autosar OS Multicore Share Resources confusion ? 2 70
C++ Code Issue 4 23
Jaspersoft Studio is a plugin for Eclipse that lets you create reports from a datasource.  In this article, we'll go over creating a report from a default template and setting up a datasource that connects to your database.
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
The viewer will learn how to use and create keystrokes in Netbeans IDE 8.0 for Windows.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

839 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