tags:

views:

336

answers:

6

Consider following class

class test
{
public:
test(int x){ cout<< "test \n"; }

};

Now I want to create array of 50 objects of class test . I cannot change class test.

Objects can be created on heap or stack.

Creating objs on stack is not possible in this case since we dont have default constructor in class

test objs(1)[50]; /// Error...

Now we may think of creating objs on heap like this..

test ** objs = NULL;
objs = (test **) malloc( 50 * sizeof (test *));
for (int i =0; i<50 ; ++ i)
{
   objs[i] = new test(1);
}

I dont want to use malloc .Is there any other way??

If you guys can think of some more solutions , please post them...

A: 

You don't need malloc(). You can use new for the array of pointers, too:

    test **objs = new test* [50];
Federico Ramponi
Just to clarify: new does dynamic memory allocation on the heap just as malloc() does. The difference is that it's built into the C++ language so it can call constructors too.
j_random_hacker
+4  A: 

Why do you need array?

std::vector<test*> v(50);

Or as @j_random_hacker suggested in the comments:

std::vector<test> v(50, test(1));

An example:

/** g++ -Wall -o vector_test *.cpp && vector_test */
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

struct Test {
  int value;
  Test(int x) : value(x) 
  {
    std::cout << "Test(" << value << ")" << " ";
  }
  operator int() const
  {
    std::cout << "int(" << value << ")" << " ";
    return value;
  }
};

int main()
{
  using namespace std;

  vector<Test> v(5, Test(1));

  cout << endl;
  copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
  cout << endl;

  v[1] = 2;
  v[2].value = 3;

  cout << endl;
  copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
  cout << endl;

  return 0;
}

Output:

Test(1) 
int(1) 1 int(1) 1 int(1) 1 int(1) 1 int(1) 1 
Test(2) 
int(1) 1 int(2) 2 int(3) 3 int(1) 1 int(1) 1
J.F. Sebastian
Don't you mean "std::vector<test> v(50, test(1))"?
j_random_hacker
@j_random_hacker: `test*` is used to allow instances of `test`'s subclasses to be put into the vector (OOP habit).
J.F. Sebastian
In many cases that's a good idea, however in this case, the fact that the OP wanted to allocate a static array of class test objects tells us that the final type *is* known at compile-time, so there's no need.
j_random_hacker
+6  A: 

You cannot create an array of objects, as in Foo foo [N], without a default constructor. It's part of the language spec.

Either do:

test * objs [50];
for() objs[i] = new test(1).

You don't need malloc(). You can just declare an array of pointers.

c++decl> explain int * objs [50]
declare objs as array 50 of pointer to int

But you probably ought to have some sort of automatic RAII-type destruction attached.


OR subclass test publicly:

class TempTest : public test
{
public:
  TempTest() : test(1) {}
  TempTest(int x) : test(x) {}
  TempTest(const     test & theTest ) : test(theTest) {}
  TempTest(const TempTest & theTest ) : test(theTest) {}
  test & operator=( const     test & theTest ) { return test::operator=(theTest); }
  test & operator=( const TempTest & theTest ) { return test::operator=(theTest); }
  virtual ~TempTest() {}
};

and then:

TempTest  array[50];

You can treat every TempTest object as a test object.

Note: operator=() & copy constructor are not inherited, so respecify as necessary.

Mr.Ree
Good post overall, but just to clarify: you say "You don't need malloc()", but you do need "new", correct? (Unless you subclass.) All I mean is that probably when the OP said "I don't want to use malloc()" he really meant "I don't want to use dynamic memory allocation."
j_random_hacker
OP malloc'ed the array of pointers. I put that array on the stack. The elements could be on the stack too, but that gets **hideous** to implement. test * objs [50]; test obj0(0), obj1(1), ..., objN(N); objs[0] = objs[1] = ... ; objs[N] =
Mr.Ree
Addendum: Reminder: If you have an array of pointers, you'd access via *(objs[i]), or use objs[i] -> method().
Mr.Ree
Be careful on how 'array' is used. Passing 'array' to a function expecting a "test*p" parameter will compile, but if sizeof (TempTest) != sizeof (test) (due to vtable added in TempTest) then "++p" will not point to the next element. The code should assert that sizeof(test)==sizeof(TempTest).
Richard Corden
++Richard. Very good point!
Mr.Ree
+2  A: 

I think that other responders are treating this question too literally.

If all you really want to do is make a "group" of 50 objects that you can treat as an array, then by far the easiest and most maintainable way of accomplishing what you're trying to do is:

std::vector<test> objs(50, test(1));

This declares a vector of 50 objects, each of which is a copy of test(1). A vector is basically a C++ growable array; although you may not need the growability, the fact that it can be called with a 2-arg constructor that copy-constructs each element is useful here.

You can use this more-or-less exactly like an array -- e.g. the 5th element is objs[4]. Performance is the same too -- the C++ standard guarantees that internally the elements are stored in a contiguous array.

j_random_hacker
A: 

Boost's Pointer Container library might come to rescue here. With boost::ptr_vector<T> you can hold a list of heap-allocated objects which can be even polymorphic (virtual functions), which isn't possible with simply std::vector<T>.

Unlike std::vector<T>, the objects won't be stored in subsequential memory addresses. Things like resizing the container however will be faster because the elements will keep their original memory addresses. The best bonus is, you don't need to call delete yourself: the contained objects will be destroyed when the ptr_vector goes out of scope. Example:

#include <boost/ptr_vector.hpp>
#include <iostream>
class test() {
protected:
    int const i;
public:
    explicit test(int i) : i(i) {}
    virtual void who_am_i() const { std::cout << "I am test " << i << std::endl; }
};
class special_test : public test {
public:
    explicit special_test(int i) : test(i) {}
    virtual void who_am_i() const { std::cout << "I am special_test " << i << std::endl; }
};
int main() {
    boost::ptr_vector<test> objs;
    for (int i=0; i<50; ++i)
        objs.push_back(new test(i)); // NB: constructing to heap here!
    objs.push_back(new special_test(123)); // objs can also hold inherited classes
    objs[13].who_am_i(); // outputs: I am test 13
    objs[50].who_am_i(); // outputs: I am special_test 123
} // all created objects are automatically destroyed here
pyrtsa
+3  A: 

Contrary to what many people believe, you can actually create an array of objects that do not have a default constructor. What you cannot do is make it use a set of arguments for all constructor invokations. You just have to initialize all elements of it. That is, you can do the following:

#define PRINTT(z, n, initializer) initializer
test objs[50] = {
    BOOST_PP_ENUM(50, PRINTT, 1) // yields 1, 1, 1, .... 1
};
#undef PRINTT

That will initialize all 50 elements with 1. boost::pp is used to print a 1 50 times in a row automatically.

Johannes Schaub - litb
The ARM (The Annotated C++ Reference Manual) (somewhat outdated, I know), stipulates that you cannot allocate an array of objects from a class that lacks a default constructor. Your solution is very clever. (+1 of course.) (Continued below.)
Mr.Ree
BUT: You are automatically allocating a set of temporary objects, automatically created (promoted) from the integers supplied, and then (depending on the compiler implementation) copying them. This only works if class "test" supports a public (or default) copy constructor.
Mr.Ree
yes, that is true. and in the way i did it, it also requires the constructor taking int is not explicit. well, you can do: test obj(1); and use BOOST_PP_ENUM(50, PRINTT, obj) . but the copy constructor is still used. c++1x will allow moving stuff into the elements. no copy required anymore then.
Johannes Schaub - litb
thanks for the praise, by the way :)
Johannes Schaub - litb
That's news to me! +1 litb.
j_random_hacker