Question about dynamically created object arrays

Hi,

I've got the famous problem, that I like to create an instance of class "parent" which has lot's of members, e.g. other child classes, but to this point of time I don't know how many of them I will need later:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  class parent
  {
    public:
      child* child;

      parent(int child_count);
  };

  int main(int argc, char** args)
  {
    parent test(20);
   return 0;
  }

  parent::parent(int child_count)
  {
    this->child = new child(this)[20];    // doesn't work like that, although I read in many topics that I'd have to do it this way...
  }

Instead, it only works when I declare "child" as array directly in class declaration, and then initialize it like that:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  class parent
  {
    public:
      child* child[100];  // bad, because I'd like to allocate only as much memory as needed

      parent(int child_count);
  };

  parent::parent(int child_count)
  {
    for(int i = 0; i < child_count; i++)
    {
      this->child[i] = new child(this); works... but WHY??
    }
  }

Why do I have to do it this way? I'd really prefer the first way because it looks much better and I wouldn't need to allocate much memory even if I don't need it later..
Any solutions?
Last edited on
You need to use initializer list when dealing with arrays:

this->child = new Child[5] {{this}, {this}, {this}, {this}, {this}}; However, I strongly suggest to use vectors, and not raw pointers:

1
2
3
std::vector<Child> children;

parent::parent(int child_count) : children(child_count, {this}) {};
This is what things like std::vector are designed to handle.

 
#include <vector> 
1
2
3
4
5
6
7
class parent
{
  public:
    std::vector <child*> children;

    parent() { }
};
1
2
3
4
parent dad;
dad.children.push_back( child("Jimmy") );
dad.children.push_back( child("Nora") );
...

If you wanted, you could even make member functions to do things like add children, which makes your code a little easier to read and maintain.

1
2
3
4
void parent::add( child* child )
{
  children.push_back( child );
}
1
2
3
parent dad;
dad.add( child("Jimmy") );
dad.add( child("Nora") );


Hope this helps.

[edit]
The one caveat is that you still need to release memory if your parent 'owns' the children, otherwise you'll have a memory leak (like in my examples above). Depending on your needs, a shared_ptr or unique_ptr may be useful.

1
2
3
4
5
class parent
{
  public:
    std::vector <std::unique_ptr <child>> children;
    ...

Good luck!
Last edited on
In the first block, the syntax is wrong in line 17; "child" must have a default constructor to be used this way.

In the second block, line 13, you're creating a single "child", so syntax is okay-ish.

Also, in the first case you were trying to create an array of "child" objects, but in the second
you made an array of "pointers to child"

To summarise, you could do :
1
2
Object *obj = new Object(arg);
Object *obja = new Object[arg];
but not
Object *obja = new Object(arg1)[arg2];

You can get around this by overloading new and delete for your class.

Alternatively, use a vector or some other dynamic container.

1
2
3
4
5
6
7
vector<child> children;
...

parent::parent(int child_count) : children(count, child(this))
{
...
}


And your vector can still grow as you wish at run-time.

Further comments: I imagine the code up there is affected, as it is not a parent-child relationship by any means, "this" is overused, and variable naming is impossible.
here is another way of doing it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "stdafx.h"
#include <vector>
#include <iostream>

using namespace std;

class child
{
	public:
	child(int i);
};

child::child(int i)
{
	std::cout << "child " << i << " created" << std::endl;
}

class parent
{

public:
	vector<child> children;
	parent(int child_count);
};

parent::parent(int child_count)
{
	children.reserve(child_count);
	
	for (int i = 0; i < child_count; i++)
	{
		child c(i);
		children.push_back(c);
	}
}

int main()
{
	parent test(20);

	int x;
	cin >> x;

	return 0;
}
Proper answers have already been given, but I would like to point out that your syntax errors were on the topic of initialization. See http://www.informit.com/articles/article.aspx?p=1852519
Hi again,

first of all, thank you all for your support!
I didn't know std::vector till now, but it sounds interesting. That's why I googled it and found some benefits (easy to handle, dynamically size growth etc.) but also a disadvantage: it needs much more memory (according to googles search results).

When I understood that right, std::vector allocates more memory then needed, and so it can additionally store new array elements ("growing").

What I'm asking myself then is, where is the difference between using std::vector and declaring child* children[100];? I mean, my motivations not to do it that way were not to allocate more memory then needed, but with std::vector I'll have the same problem, or am I wrong?

Otherwise, I recognize that almost every reply to my question contains std::vector as suggestion, so I think it's still better to use it then declaring an array with fixed and very big size...
What do you mean?

@MiiNiPaa:
this->child = new Child[5] {{this}, {this}, {this}, {this}, {this}};
How could I manage this with a loop? I mean, initializing 100 child object with that method would be really hard to type by hand {{this}, {this}........}...
Last edited on
it needs much more memory (according to googles search results).
What? At most 32 bytes per vector.

my motivations not to do it that way were not to allocate more memory then needed, but with std::vector I'll have the same problem, or am I wrong?
You can specify size of the vector when creating it. After that it will get more memory only when needed. Additionally it is safe: it manages memory and release it. You cannot forget about this, you should not worry about exception thrown before you have a chance to release memory, it is easy to extend your class if you need, It provides many useful functions.

How could I manage this with a loop?
If your Child class have an assigment operator and default constructor, you can do:
1
2
3
4
5
this->child = new Child[100]; //default constructs 100 objects
for(std::size_t i = 0; i < 100; ++i)
    child[i] = Child(this); //Creating a temporary and copy-assign it. 100 times
//Whole affair is 3 times as slow as vector one. 
//With some changes we can improve it to 2x 
closed account (48T7M4Gy)
A vector is the preferred container in C++ because, in comparison to arrays, vector array control and management is substantially better and easier to implement.

Vectors give you more flexibility, but admittedly real parents don't have more than, lets say, 25 kids?? 100 would be very unusual even for ...

A good summary of the pros and cons is:
http://www.cplusplus.com/reference/vector/vector/

PS there are other standard containers and a <map> might be useful.


Last edited on
@MiiNiPaa:
Hehe well, you convinced me with that :)
I'm really interested in how std::vector manages that all?! How can it dynamically add memory to an array without copying it to a new, bigger one? Or is it that way? Then I'm wondering how it can still be faster 3 or 2 times then the way you specified up there...

You can specify size of the vector when creating it

Is that advisable or not necessary?
Last edited on
Is that advisable or not necessary?
Depending on your needs. If you need exactly x values initialized to some value y, you probably want to specify it when creating vector.

Then I'm wondering how it can still be faster 3 or 2 times then the way you specified up there...
With arrays: you are creating n default-initialized objects (1). Creatint n custom initialized objects (2) and assigning them to array elements (3). You can simplify it creating one object and reusing it throughout the loop, eliminating (2).
Vector: creates uninitialized storage (neglible), value initializes each element (1).
You can do this too, but it will be more complexthat simply declare an array, so it is better to leave management to something which is already created, tested and optimized.

How can it dynamically add memory to an array without copying it to a new, bigger one?
It is actually reallocates it. That is why you want to move-optimize your classes if you want to store them in containers. But, you do not use dynamic growing abilities of vector. You just need fixed size something to store values in.
thanks a lot, that helped!!
Topic archived. No new replies allowed.