Thread: Glad this board is back! Vector question

  1. #1
    Registered User
    Join Date
    Mar 2003
    Posts
    72

    Glad this board is back! Vector question

    Glad this place is back online, I missed it. I have a question. I am working on an exercise that requires that I create a genVector class that has a base class of Vector. It works as such, for example:

    Code:
    genVector<int> (-5,5); // assigns numbers from -5 to 5 in a Vector
    In order to this, the exercise requires that I overload the [] operator, create a resize member function, and (of course) have a constructor. Below is my code (header file genvec.h) and my file that I am using to test the class:

    genvec.h:

    Code:
    #include <vector>
    
    #include <iostream>
    
    #include "d_except.h"
    
    using namespace std;
    
    template <typename T>
    class genVector:public vector<T>
    {
    	public:
    		genVector(int low, int high):vector<T>(lower,upper)
    		{
    
    		lower=low;
    		upper=high;
    		}
    
    		T& operator[] (int i)
    		{
    			if ((i < lower) || (i > upper))
    				throw indexRangeError(
    				"genVector: index range error",i,size());
                i-=lower;
    			return vector<T>::operator [] (i);
    		}
    
    		void resize(int lowIndex, int highIndex)
    		{
    			genVector(lowIndex, highIndex);
    		}
    
    	private:
    		int lower;
    		int upper;
    };
    Code I am using to test the class:
    Code:
    #include <iostream>
    #include "genvec.h"
    #include "d_util.h"  // Contains writeVector functionality
    
    using namespace std;
    
    int main()
    {
    	genVector<double> tempVector (-10,25);
      
    	writeVector(tempVector);
    
    
    	return 0;
    }
    When I try to compile this, it says that the program encounters an error and needs to close and I get no output. In my past experiences, this usually means that I have some sort of overflow or a memory leak.

    I am guessing that part of the reason that I might have a memory leak is because my class has no destructor? Is a destructor actually necessary for this class?

    If someone could answer that and perhaps give me any pointers to get me in the right direction, I'd be very grateful.

    Thanks!

  2. #2
    Senior Member joshdick's Avatar
    Join Date
    Nov 2002
    Location
    Phildelphia, PA
    Posts
    1,146
    As a rule, if you need to provide a constructor, assignment operator or destructor, you need to implement all of those together.

    I take issue with your design. Why do you have ranges from an arbitrary negative number to an arbitrary positive number? The convention for arrays and vectors is to number from 0 to n-1.

    For what will you be using your class?
    FAQ

    "The computer programmer is a creator of universes for which he alone is responsible. Universes of virtually unlimited complexity can be created in the form of computer programs." -- Joseph Weizenbaum.

    "If you cannot grok the overall structure of a program while taking a shower, you are not ready to code it." -- Richard Pattis.

  3. #3
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    >> i-=lower

    eh? Why go below the minimum? That's dangerous.

    Code:
     void resize(int lowIndex, int highIndex)
    {
     genVector(lowIndex, highIndex);
    }
    That code simply creates a nameless genvector object (which gets destroyed imeediately after function exit). It will not resize the genvector you intend to..
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  4. #4
    Registered User
    Join Date
    Mar 2003
    Posts
    72
    "I take issue with your design. Why do you have ranges from an arbitrary negative number to an arbitrary positive number? The convention for arrays and vectors is to number from 0 to n-1.

    For what will you be using your class?"

    The class specified in my original post is an exercise out of the book that I am using currently on data structures. I know that typically you don't have negative numbers in a vector and that is part of the purpose (I believe) of the book wanting me to make a genVector inherited class with a Vector base class, so that it can have that additional functionality.

    Please let me know if I explained that well, as I am kind of tired and it may be incoherent...

  5. #5
    Registered User
    Join Date
    Mar 2003
    Posts
    72
    "That code simply creates a nameless genvector object (which gets destroyed imeediately after function exit). It will not resize the genvector you intend to."

    Would you be willing and/or able to provide some pointers on a better resize function that will accomplish what I am after? I looked at the resize functionality in the actual Vector (base) class and I honestly had a real difficult time deciphering what it was doing..

    Thanks!

  6. #6
    Registered User
    Join Date
    Mar 2003
    Posts
    72
    Here's the exact text from the exercise in the book that I am trying to figure out. This should (hopefully) explain better what I am trying to accomplish:

    Implement a class genVector that generalizes the vector class to create a safe array with general starting and ending indices. For instance,

    Code:
    genVector<int> vA(1,10), vB(-1,8);
    creates objects vA and vB with index ranges 1 <= i <=10 and -1 <i < 8 , respectively. Objects of type genVector can be indexed within their defined range. For instance,

    Code:
    int  i;
    for (i=-1; i<=8; i++) // initalize all vector elements to 0
    vB[i]=0;
    Derive genVector from the vector class by using public inheritance. Override the index operator so it accepts indices in the correct range. Implement a derived member function resize() that resizes the vector and resets the beginning and ending indices. These actions prevent references to the vector index operator and resize() function unless the programmer uses the scope operator "::".

    Code:
    template <typename T>
    class genVector: public vector<T>
    {
      public:
           genVector(int low, int high);
           // vector has high - low+1 elements in range [low,high]
           
           T& operator [] (int i);
           // operator verifies that lower <= i <= upper. 
           // if not, it throws the indexRangeError exception
    
           void resize(int lowIndex, int highIndex);
           // resize vector and set range to [lowIndex, highIndex]
    
       private:
    
             int lower;
             int upper;
    };
    Place genVector in header file "genvec.h" and write a program that declares the following objects:

    Code:
    genVector<char> ucLetter(65,90);
    genVector<double> tempVector(-10,25);
    Initialize ucLetter so that ucLetter[65]='A',...,ucLetter[90]='Z'. Initialize tempVector so that tempVector[t] is the Fahrenheit equivalent of Celsiius tempature t. Recall that

    Code:
    F=9.0/5.0 * C+32
    Display the contents of each vector.


    The information listed above is the framework that is provided by the exercise. Hopefully this helps to explain where I'm coming from and why I am approaching things as I am...

    I am still trying to figure out the resize() function and the constructor for this, but I think I have the overloaded [] operator pretty close to correct if not exactly right.

    Any pointers/assistance would be very much appreciated. I can re-post my code also, if needed.

    Thanks for your time!

  7. #7
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    Here's my two cents worth. May not make complete sense, but use it as you will.

    size() returns the number of elements in the vector and capacity() returns the maximal number of possible elements in the vector. You can change capacity of the vector using reserve() and the size of the buffer using resize(). resize() is probably implemented something like this:

    Code:
    resize(int newSize, T defaultValue)
    {
       //call reserve(newSize);
       //loop to put default value in new elements
       //assign  newSize to size if appropriate;
    }
    the following creates a vector of ints called v, which will have capacity of 2 ints both of with the value of 1. Then v is resized to to capacity of 5 instead of 2 and 0 is placed in the 3 new elements added to the end of vector.

    vector<int> v(2, 1);
    v.resize(5, 0);

    What's going on under the hood here for the vector class and how can you use it for your new class?

    As I understand it, the vector class has a private member which is an array declared using a pointer and dynamic memory. Let's say the name of the array is buffer. The amount of dynamic memory used for buffer is based on a default value if no value is given in the constructor. I have no idea what the default value is. I used the value of 2 to set the capacity of v to 2 in the above code. Each element is assigned the value of the second parameter in the constructor if such a value is given. Above the value is 1.

    To change the size of buffer you call resize() giving the new capacity and the value to place in the new memory when allocated. the new capacity is passed to the reserve() method of the vector class which changes the capacity of the vector to that value, which is 5 in the above code. Within reserve() a temporary array of ints of length 5 is declared using dynamic memory. All the elements in buffer are copied into the temporary array in order in the same indicies as in buffer. then the memory in buffer is deleted. buffer is then allocated memory for 5 ints using the new operator. the elements in the temporary array, which originally were in buffer, are copied back into buffer in order and using the same indicies. Then the memory for the temporary array is deleted and the pointer used to create the temporary array is deleted when it goes out of scope at the end of the reserve() function.

    Back in resize() for the vector class, a loop is probably used to fill the new elements with the new default value, if one is given, as it is above. resize() would then set the value of size to the new capacity since all the elements are now filled. The value of size is left alone if resize() isn't given a default value to use for the new memory allocated, although if the new elements aren't filled with some default value you might as well use reserve() rather than resize().

    In your case, given the skeleton provided resize() will:
    1) call reserve() with the value of the highIndex - lowIndex + 1
    2) assign the value of highIndex to upper
    3) assign the value of lowIndex to lower
    4) leave the value of size unchanged since no default value to assign to the new elements is given.
    5) reserve() should probably be overriden such that memory in buffer is expanded using a scheme similar to the above discussion, and the elements in buffer are copied back and forth such that they remain using the same indicies as they did in the original form of buffer, even if both ends of buffer are extended. Any new memory added to either end of buffer is left empty. What to do if lowIndex is greater than lower or highIndex is less than upper is up to you, but in the vector class, I don't think capacity and size are changed if the new capacity is smaller than the original capacity.

  8. #8
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    The textbook requires it, otherwise I wouldn't recommend deriving from vector as public, it lacks a virtual destructor. You must never cast a pointer or reference to genVector to one to vector and try to use delete on it.


    There are other issues with the many functions vector has that you need to somehow catch in order to adjust the size of the vector correctly.

    I think you should ask for clarification on this issue: vector has functions such as insert which can take a range of objects, these break the internal consistency of your class unless correctly handled, so your class should hide them if it plans to use them. However, hiding them means private inheritance.

    Your constructor's main problem is that std::vector doesn't have a constructor taking two integers. The one you want takes only one integer, the size of the vector. For a normal vector, the lowest index is always 0. The size of a genVector is of course the difference between the bounds.
    Another problem is that you call the base constructor using the member variables lower and upper, but they are undefined at the time of the base constructor call.
    Code:
    genVector(int low, int high) :
      std::vector<T>(high-low), lower(low), upper(high)
    {
    }
    The resize function is very similar. You call vector's resize and again pass the difference between high and low, then you assign these values to the member variables.


    But the problem remains. push_back, for one, breaks your container. The new element is inaccessible, because it has an index that is invalid for your class.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  9. #9
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    >> But the problem remains. push_back, for one, breaks your container. The new element is inaccessible, because it has an index that is invalid for your class.

    I would argue that if push_back for genVector class overrides push_back for vector class then variable index may be valid in genVector even if it isn't in vector. Calling push_back or resize using the dot operator will get the genVector version of push_back or resize. Using genVector variable followed by dot operator followed by base class name followed by scope operator followed by method name will call vector version of push_back or resize. The genVector version of the method may well be valid, eventhough calling the vector method may not.
    Code:
    template <typename T>
    class genVector : public vector
    {
       ...
       public:
          resize(int, T);
          ....
    };
    genVector gv(-1, 8);

    gv.resize(-5, 9); //call genVector version of resize. increases capacity from 10 to 15 and changes valid range of indices for gv but doesn't change size of gv.

    gv.vector::resize(15, 7); //call vector version of resize. increases capacity to 15 and size of gv to 15 and assigns 7 to all new elements, but may not work given index range of gv is not valid index range for a vector. How would the indexes for the new elements be distributed if it did work? Probably from index 9 up, without extending downside range at all, but I don't think it will work, though I can't say that for sure.

    gv.vector::resize(-5, 10); //PROBABLY won't work since can't have a negative size to a vector.

    _____________________________________
    >> Your constructor's main problem is that std::vector doesn't have a constructor taking two integers.

    True, technically, but

    vector<T> name(size, T)

    is available, and if it's a vector of ints then

    vector<int> v(2, 1);

    is a valid statement but

    vector<char> v1(2, 1);

    isn't valid although

    vector<char> v2(2, '1');

    is valid.

    my initial attempt at a constructor for genVector would look something like this:
    Code:
    template<typename T>
    class genVector : public vector
    {
        private:
           //T * buffer;  inherited from vector
           int upper;
           int lower;
        public:
          genVector(int, int);
          ~ genVector() {delete [] buffer;};
          void resize(int, int);//overrides all versions of resize in vector class
    };
          
    genVector::genVector(int high, int low) : upper(high), lower(low)
    {
       buffer = new T[high - low + 1];  //T must have a default constructor
    }

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    I would argue that if push_back for genVector class overrides push_back for vector class then variable index may be valid in genVector even if it isn't in vector.
    The specification doesn't require them to override push_back. And there's still the vector:: method of calling the base version. This shouldn't be possible, as a proper class may not permit operations that break its internal consistency.

    True, technically, but
    Nice but. Yeah, I know, but this doesn't do what is intended, so it only makes the original problem worse.

    my initial attempt at a constructor for genVector would look something like this:
    Very bad. You are comitting no less than three mortal C++ sins here
    1) You're relying on the internal implementation of a class that you didn't write.
    2) You're breaking the internal consistency of this class by ignoring its public interface.
    3) You're allocating memory for the use of a standard container without the container's allocator.

    And as the cream on top of it it won't compile because every sane STL has the members of vector declared as private, which means you can't access them.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. function trouble
    By rebel in forum C++ Programming
    Replies: 4
    Last Post: 12-21-2005, 05:23 AM
  2. Projects board
    By VirtualAce in forum A Brief History of Cprogramming.com
    Replies: 8
    Last Post: 12-13-2004, 09:33 AM
  3. Welcome to the FAQ Board
    By kermi3 in forum FAQ Board
    Replies: 0
    Last Post: 11-01-2001, 10:33 AM
  4. Where's the old board...?
    By Cheeze-It in forum A Brief History of Cprogramming.com
    Replies: 2
    Last Post: 09-18-2001, 01:16 PM