2

I want to initialize an array of struct type. Currently i am doing this

struct Gene 
{
    int **boxes;
    int fitness;

    Gene()
    {
        boxes = new int*[get_the_number]; 
        for (int i = 0; i < get_the_number; ++i) {
            boxes[i] = new int[get_the_number];
        }
    }
};

and I am initializing like this

Gene * P = new Gene [t];

It is working fine and i can do my work. It calls the default constructor which i have written itself and work is done. But now, i want to pass a parameter into the default constructor. I changed my constructor to this.

Gene(int n)

But I don't know how to initialize it, i did this

Gene * P= new Gene (2) [t];

But it gives me error that "No suitable conversion from Gene* to Gene exists". I know that I can write the setter function and do what I want to do there or instead of making an array of Gene type I can make an array of Gene* and at each index I can initialize new Gene(2). But I don't want to do because I don't need to pass a variable. I just wanted to know that can't I call my constructor which takes a parameter.

Can't I do something like Gene * P= new Gene(2)[t];?

I mean the compiler is calling that constructor which doesn't take a parameter. Can't it call this constructor instead of that? Thank you,

p.s.: I know its a beginners question but I am back to C++ after a long time so I don't quiet remember things.

2
  • 1
    learn to use std::vector instead. Commented Apr 22, 2014 at 19:19
  • What you want to achieve is possible with pure pointers, but that kind of stuff is generally done only when you implement std::vector-like class. Commented Apr 22, 2014 at 19:28

5 Answers 5

3

First, I would second the suggestion to use std::vector instead of calling new[] and delete[] yourself. You will save yourself a lot of trouble.

Second, I can think of a way to do what I think you want. I can't imagine what kind of syntax you would expect if each object were to be created with different parameters (similar to Lisp's map), so I'm going to assume that you want each object to be created with the same parameters, or with easily computed parameters.

std::vector with all identical objects:

#include <vector>

std::vector<Gene> P(size, Gene(2));

std::vector with similar but not identical objects:

#include <algorithm>
#include <iterator>
#include <vector>

int foo = 0;
std::vector<Gene> P;
// you can omit the next line; back_inserter will make sure the vector grows appropriately
P.reserve(size); 
std::generate_n(std::back_inserter(P), size, [&foo](){ Gene(++foo); });

You can get a lot fancier in the lambda that creates a Gene object, or you could use a normal function instead of a lambda.

new[] with identical objects:

#include <algorithm>
#include <iterator>

Gene* P = new Gene[size];
std::fill_n(P, size, Gene(2));

new[] with similar objects:

#include <algorithm>
#include <iterator>

int foo = 0;
Gene* P = new Gene[size];
std::generate_n(P, size, [&foo]() { Gene(++foo); });

Both cases with new[] create default objects that are immediately overwritten with fill_n or generate_n (on a decent implementation, the vector approach should not have this problem). That's probably nothing to worry about, but I feel an obligation to mention it. The bigger problem is that writing exception safe code with plain new[] and delete[] is nearly impossible. Writing exception safe code with vector is far easier.

And, yes, I would recommend the same kind of changes to Gene for the same reason (as of now, Gene simply isn't exception safe; which is bad given that new[] reports failure by throwing an exception):

#include <algorithm>
#include <iterator>
#include <vector>

struct Gene {
    std::vector<std::vector<int>> boxes;
    int fitness;
    static const size_t DEFAULT_NUMBER_OF_BOXES = 20;

    Gene() : boxes(DEFAULT_NUMBER_OF_BOXES), fitness(0)
    {
        std::for_each(boxes.begin(), boxes.end(),
                      [](std::vector<int>& b) { b.resize(DEFAULT_NUMBER_OF_BOXES); });
    }

    Gene(size_t N) : boxes(N), fitness(0)
    {
        std::for_each(boxes.begin(), boxes.end(),
                      [N](std::vector<int>& b) { b.resize(N); });
    }
};
Sign up to request clarification or add additional context in comments.

Comments

2

What you are doing is likely not what you want. Do you have good reason to really use all of those pointers? There is a reason std::vector was invented and best practices (as this seems to be a beginner question, this seems relevant) demand you to start using some C++ goodness instead of those pesky C pointers.

With std::vector, you can specify the default element you want your vector with:

Gene gene(<some gene constructor call here>);

The following vector constructor call constructs a vector of genes with 17 identical copies of gene

std::vector<Gene> gene_pool(17, gene); 

p.s.: If you even know the size of the vector/array/list/container at compile time as others are suggesting, have a look at std::array.

Comments

2

There's no simple way to do this, new[] only allows default or no initialization.
You could have a for and initialize each of them (which sounds to me like the easiest solution), but there are also more complicated ways to create the in memory objects as (void*) and use reinterpret_cast to cast them to Gene* but in the end you'll still need to call the c-tors for each of the created instance of the classes

2 Comments

I would clarify that it's actually new[] that only allows for default initialization - there's nothing wrong with ptr = new SomeClass(that, takes, many, arguments, in, the, constructor); for a single object... It's the array case that has the restriction, because there's no convenient way to specify different sets of constructor arguments for each element in the array...
Yeah twalberg you are right, its only with the array
2

If you like to avoid new/delete in your code:

#include <vector>

class Gene
{
    public:
    std::size_t extent;
    std::vector<int> boxes;

    Gene(int n) : extent(n), boxes(n*n, int(0)) {}
    int operator () (unsigned row, unsigned col) {
        return boxes[row * extent + col];
    }
};


int main()
{
    std::size_t t = 42;
    std::vector<Gene> P(t, Gene(2));
    return 0;
}

Note: Each Gene has it's own boxes (initialized with zero) after construction.

Comments

-3

First of all, don't use new unless you really don't know the size of the array until runtime.

If you use a static array, you can use the array's initialization list:

Gene array[x] = {Gene()};

Alternatively, you can use memcpy:

Gene default;
for (int i=0; i < size; i++} 
{
   memcpy (&(array[i]), &default, sizeof(Gene));
}

The third option is to use a vector

std::vector<Gene> array(size, Gene());

7 Comments

memcpy is not an appropriate suggestion for C++.
It's not appropriate for people who don't know how to use it.
It's not appropriate, period. std::move or operator =() would be more appropriate
@user2036161 OP most certainly doesn't know how to use memcpy in C++; if you suggest using it, you should explain when it's safe to (e.g. if the type in question is a POD, which it isn't).
It would be arbitrary to assume the OP cannot use the current C++ standard. And in C++03, you'd still want to use std::copy or operator =(). The only reason to use memcpy in C++ is when you want to copy some odd-sized slice of memory blob, not ever for copying whole objects.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.