6

I'm reading in values from a file which I will store in memory as I read them in. I've read on here that the correct way to handle memory location in C++ is to always use new/delete, but if I do:

DataType* foo = new DataType[sizeof(DataType) * numDataTypes];

Then that's going to call the default constructor for each instance created, and I don't want that. I was going to do this:

DataType* foo;
char* tempBuffer=new char[sizeof(DataType) * numDataTypes];
foo=(DataType*) tempBuffer;

But I figured that would be something poo-poo'd for some kind of type-unsafeness. So what should I do?

And in researching for this question now I've seen that some people are saying arrays are bad and vectors are good. I was trying to use arrays more because I thought I was being a bad boy by filling my programs with (what I thought were) slower vectors. What should I be using???

schnozzinkobenstein
  • 795
  • 1
  • 8
  • 14

7 Answers7

8

Use vectors!!! Since you know the number of elements, make sure that you reserve the memory first (by calling myVector.reserve(numObjects) before you then insert the elements.).

By doing this, you will not call the default constructors of your class.

So use

std::vector<DataType> myVector; // does not reserve anything
...
myVector.reserve(numObjects); // tells vector to reserve memory
villintehaspam
  • 8,540
  • 6
  • 45
  • 76
  • Vectors are usually a good option, but they still don't let you call a constructor of your choice. Vector elements are always copy-constructed. – Ben Voigt Jan 01 '11 at 23:39
  • True! You can then use a vector of smart pointers to your objects if you would like (and hence the copy constructor of the smart pointer will be called instead). – villintehaspam Jan 01 '11 at 23:41
  • @Ben Voigt: in C++0x, wouldn't `emplace_back` allow you to use the constructor of your choice ? – Matthieu M. Jan 02 '11 at 13:44
  • @Matthieu: Correct, C++0x will finally fill in this missing piece (I use the future tense because I think this capability isn't implemented in any compilers yet). – Ben Voigt Jan 02 '11 at 15:06
8

You can use ::operator new to allocate an arbitrarily sized hunk of memory.

DataType* foo = static_cast<DataType*>(::operator new(sizeof(DataType) * numDataTypes));

The main advantage of using ::operator new over malloc here is that it throws on failure and will integrate with any new_handlers etc. You'll need to clean up the memory with ::operator delete

::operator delete(foo);

Regular new Something will of course invoke the constructor, that's the point of new after all.

It is one thing to avoid extra constructions (e.g. default constructor) or to defer them for performance reasons, it is another to skip any constructor altogether. I get the impression you have code like

DataType dt;
read(fd, &dt, sizeof(dt));

If you're doing that, you're already throwing type safety out the window anyway.

Why are you trying to accomplish by not invoking the constructor?

Logan Capaldo
  • 39,555
  • 5
  • 63
  • 78
  • What's the advantage of calling `::operator new` directly vs `new char[]` like the question already mentions? – Ben Voigt Jan 02 '11 at 03:03
  • @Ben Voigt, communicating intent mostly in this case. You're not trying to create an array of `char's, you're allocating some memory. The second issue is IIRC array new will initialize PODs to 0, which carries some slight overhead. – Logan Capaldo Jan 02 '11 at 03:12
  • Seems to be default-initialized which, since `char` is not a class type, does nothing. Unfortunately I have only the C++0x draft standard in front of me and this is definitely one of the things being changed (in C++0x braced-initializers are allowed on array new). – Ben Voigt Jan 02 '11 at 03:18
2

You can allocate memory with new char[], call the constructor you want for each element in the array, and then everything will be type-safe. Read What are uses of the C++ construct "placement new"?

That's how std::vector works underneath, since it allocates a little extra memory for efficiency, but doesn't construct any objects in the extra memory until they're actually needed.

Community
  • 1
  • 1
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
1

You should be using a vector. It will allow you to construct its contents one-by-one (via push_back or the like), which sounds like what you're wanting to do.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
1
vector<DataType> dataTypeVec(numDataTypes);

And as you've been told, your first line there contains a bug (no need to multiply by sizeof).

hillel
  • 2,343
  • 2
  • 18
  • 25
  • You might want to test that before you claim that the default constructor isn't called. (It is, once, and then the copy constructor is called `numDataTypes` times.) – Ben Voigt Jan 01 '11 at 23:42
1

I think you shouldn't care about efficiency using vector if you will not insert new elements anywhere but at the end of the vector (since elements of vector are stored in a contiguous memory block).

Vladimir
  • 1,781
  • 13
  • 12
1

Building on what others have said, if you ran this program while piping in a text file of integers that would fill the data field of the below class, like:

./allocate < ints.txt

Then you can do:

#include <vector>
#include <iostream>

using namespace std;

class MyDataType {
public:
  int dataField;
};


int main() {

  const int TO_RESERVE = 10;

  vector<MyDataType> everything;
  everything.reserve( TO_RESERVE );

  MyDataType temp;
  while( cin >> temp.dataField ) {
    everything.push_back( temp );
  }

  for( unsigned i = 0; i < everything.size(); i++ ) {
    cout << everything[i].dataField;
    if( i < everything.size() - 1 ) {
      cout << ", ";
    }
  }
}

Which, for me with a list of 4 integers, gives:

5, 6, 2, 6
Michael Rivera
  • 193
  • 1
  • 7