1

i am new to classes in C++ and i need to create a class "Plot" which has a method that reads in data from a file and creates a 3d grid.

i understand that you can make "default" constructors with default values or you can create a special constructor with predefined values.

in my "private" section, i have:

int nx; // number of "x" values
int ny; // number of "y" values
int nz; // number of "z" values
double* grid; // one dimensional array which stores nx*ny*nz values
double tgrid(int ix, int iy, int iz); // function which searches grid and returns value

now, i want to create my "plot" object and then AFTER that, dynamically create the "grid" array. is it possible to do this? or will i need to declare the size of the array "grid" when i first create "plot"?

drjrm3
  • 4,474
  • 10
  • 53
  • 91
  • 1
    Please explain your reasoning for choosing C++ as your language but wanting to "stay away" from using `vector` as your container of choice. – John Dibling Sep 27 '11 at 16:10
  • this is an opengl project and all my previous code is in c++, so i am sticking with that rather than re-writing everything. the reason i want to "stay away" from vector is because i don't know how it works, so i feel less control over that than i do something i write myself and understand every bit of what is gong on. that being said, after reading everyone's comments, i think i will do more research on vector so i can become more familiar with it's implementation. – drjrm3 Sep 27 '11 at 17:48

4 Answers4

4

Use std::vector grid; as your member. Then you can use grid.resize(nx*ny*nz) to force the size you want or use grid.push_back(value); for each value you want to add to the array.

Mark B
  • 95,107
  • 10
  • 109
  • 188
2

It is possible:

class Plot{
   int nx; // number of "x" values
   int ny; // number of "y" values
   int nz; // number of "z" values
   double* grid; // one dimensional array which stores nx*ny*nz values
   double tgrid(int ix, int iy, int iz); // function which searches grid and returns value
public:
   Plot()
   {
      grid = NULL;
   }
   ~Plot()
   {
       delete[] grid;
   }
   void init(int x, int y, int z)
   {
      delete[] grid; //make sure no memory leaks since grid might have already been allocated
      nx = x;
      ny = y;
      nz = z;
      grid = new double[nx*ny*nz];
   }
};

After the construction, just call the method init:

Plot p();
p.init(2,3,4);

EDIT:

You should however consider Mark B's answer. I would also use something from std:: rather than a dynamically allocated array. Much easier to manage.

EDIT2:

As per Constantinius' answer, avoid init() methods when you can. If you specifically need the initalization AFTER construction, use it, otherwise keep all initialization logic in the constructor.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • IMHO, the `init` function is a bad idea, the CTOR should take care of that. – Constantinius Sep 27 '11 at 15:04
  • I know, but please read the question again: "i want to create my "plot" object and then AFTER that, dynamically create the "grid" array" – Luchian Grigore Sep 27 '11 at 15:05
  • 1
    The grid should have been initialized to NULL in the constructor, but this answer is the best (a std::vector might be relevant but it is not the silver bullet). – Clodéric Sep 27 '11 at 15:07
  • You should read the question again ;) : "i am new to classes in C++". And it's a bad idea, because you can easily misuse the class, create memory leaks and crashes. Including the allocation into the CTOR would take care of the problem and would not contradict the requirements. – Constantinius Sep 27 '11 at 15:07
  • come to think of it, this may be the best answer so far ... but instead of "init" - this would really go in my "read" method, as i will know the values of nx, ny, nz only AFTER opening the file and reading the first few numbers ... i will ask for a bit of clarification, however ... i understand that the destructor is needed to delete the allocated memory, but when you call "delete" inside "init" (which is your version of what i will do in "read") what if grid has not been created yet? will delete grid; cause problems if i have not created it yet? – drjrm3 Sep 27 '11 at 15:21
  • 1
    You're missing a copy constructor and assignment operation, which makes the class rather dangerous to use. Also, `init()` can leave the object in an invalid (and indestructible) state if memory allocation fails. As you say in your edit, it really is much easier to use `std::vector` than to try to implement it yourself. – Mike Seymour Sep 27 '11 at 17:22
  • To expand on @MikeSeymour's comment re:copy constructor & assignment operator, see [Rule of Three](http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)). – Robᵩ Sep 27 '11 at 17:26
1

It depends on how your Plot class should be used. If you consider it necessary to create objects of this class only with valid sizes you should not allow a default constructor. You do this by defining your own constructor like that:

public:
Plot(int _nx, int _ny, int _nz) : nx(_nx), ny(_ny), nz(_nz) 
{
    // initialize your array
    grid = new double[nx*ny*nz];
}

also don't forget your destructor to clear the allocated memory:

~Plot() 
{
    delete[] grid;
}
Constantinius
  • 34,183
  • 8
  • 77
  • 85
  • the problem is that this is exactly what i do NOT want to do. i know that it would be best to create Plot with a predefined nx, ny, nz, but i need to create it first, THEN assign values to them after. i have seen the suggestion to use but i would like to keep from that if possible. (emphasis added only to show intent - not being a brat!) – drjrm3 Sep 27 '11 at 15:14
  • why don't you create the `plot` right when you know the sizes? I don't see when it would make sense to create an invalid object. – Constantinius Sep 27 '11 at 15:19
  • well, i need the reading of the data to be a METHOD in the class PLOT. thus, PLOT will only be able to call METHOD after it has been constructed, correct? – drjrm3 Sep 27 '11 at 15:24
  • Yes this is correct. But I do not understand the reason of this restriction. If is not circumventable you should refer to the other posters solutions. Good luck! – Constantinius Sep 27 '11 at 15:48
0
class Plot {
    int nx; // number of "x" values
    int ny; // number of "y" values
    int nz; // number of "z" values
    double* grid; // one dimensional array which stores nx*ny*nz values
    double tgrid(int ix, int iy, int iz); // function which searches grid and returns value
public:
    /* default constructor */
    Plot() : nx(0), ny(0), nz(0), grid(NULL) { }
    /* rule of five copy constructor */
    Plot(const Plot& b) : nx(b.nx), ny(b.ny), nz(b.nz) {
        int max = nx*ny*nz;
        if (max) {
            grid = new double(max);
            for(int i=0; i<max; ++i)
                grid[i] = b.grid[i];
        } else
            grid = NULL;
    }
    /* rule of five move constructor */
    Plot(Plot&& b) : nx(b.nx), ny(b.ny), nz(b.nz) grid(b.grid) { b.grid = NULL; }
    /* load constructor */
    Plot(std::istream& b) : nx(0), ny(0), nz(0), grid(NULL) { Load(b); }
    /* rule of five destructor */
    ~Plot() { delete[] grid; }
    /* rule of five assignment operator */
    Plot& operator=(const Plot& b) {
        int max = b.nx*b.ny*b.nz;       
        double* t = new double[max];
        for(int i=0; i<max; ++i)
            t[i] = b.grid[i];            
        //all exceptions above this line, NOW we can alter members
        nx = b.nx;
        ny = b.ny;
        nz = b.nz;
        delete [] grid;
        grid = t;
    }
    /* rule of five move operator */
    Plot& operator=(Plot&& b) {   
        nx = b.nx;
        ny = b.ny;
        nz = b.nz;
        delete [] grid;
        grid = b.grid;
        b.grid = NULL;
    }
    /* always a good idea for rule of five objects */
    void swap(const Plot& b) {
        std::swap(nx, b.nx);
        std::swap(ny, b.ny);
        std::swap(nz, b.nz);
        std::swap(grid, b.grid);
    }

    /* your load member */
    void Load(std::istream& in) {
        //load data
        //all exceptions above this line, NOW we can alter members
        //alter members
    };
};

int main() {
     Plot x;  //default constructor allocates no memory
     Plot y(x); //allocates no memory, because x has no memory
     Plot z(std::cin); //loads from stream, allocates memory
     x = z; //x.grid is _now_ given a value besides NULL.
}

This answers your questions I think. Still: use a std::vector.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158