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.

jww
  • 97,681
  • 90
  • 411
  • 885
Usman Mahmood
  • 671
  • 1
  • 7
  • 12

5 Answers5

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); });
    }
};
Max Lybbert
  • 19,717
  • 4
  • 46
  • 69
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

Max Lybbert
  • 19,717
  • 4
  • 46
  • 69
IonutG
  • 51
  • 2
  • 1
    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... – twalberg Apr 22 '14 at 19:09
  • Yeah twalberg you are right, its only with the array – Usman Mahmood Apr 22 '14 at 19:49
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.

niklasfi
  • 15,245
  • 7
  • 40
  • 54
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.

-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());
ventsyv
  • 3,316
  • 3
  • 27
  • 49
  • 2
    `memcpy` is not an appropriate suggestion for C++. – tenfour Apr 22 '14 at 19:09
  • 1
    It's not appropriate for people who don't know how to use it. – ventsyv Apr 22 '14 at 19:17
  • It's not appropriate, period. `std::move` or `operator =()` would be more appropriate – tenfour Apr 22 '14 at 19:21
  • 1
    @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). – anatolyg Apr 22 '14 at 19:24
  • std::move was added with C++11, what if your project is not using C++11? Assignment is fine, I grant you that, but there is nothing wrong with memcpy. – ventsyv Apr 22 '14 at 19:26
  • 1
    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. – tenfour Apr 22 '14 at 19:30
  • Some projects and companies do refuse to use C++11; until recently, Google was one such company ( http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#C++11 ). However, it's worth mentioning that C++11 is now three years old and there have been free implementations available for most of that time. I think it's generally reasonable to assume that a questioner can use C++11 unless they say otherwise. – Max Lybbert Apr 23 '14 at 08:21