0

I am relatively new to creating object vectors. The below is the code that my object used prior to me including a holding vector:

class obj {
public:
    int* arr;
    obj(int x) {
        arr = new int[x];
    }
    ~obj() {
        delete[] arr;
    }
    // All functionality stripped for clarity
};

I would now like to create a vector to hold all of the created objs. What I have tried is to create a vector and just push the newly created objects into it akin to the below:

std::vector<obj> objVector;
objVector.push_back(obj(5));
objVector.push_back(obj(8));

The above leads to errors where arr has not been created and is an empty pointer. The suggestion I have seen elsewhere on this site is to include a copy creator to facilitate. So I have the below questions:

  • Using the above push_back code will there be 2 objects created (A temporary one and the vector one) or will one be created directly into the vector.
  • What do I have to include in the copy creator, Will I have to create a new arr for the second object or could I just pass it onto the second.

Also if this is a poor way to implement a holder of objects then please could you point me towards a source where I could read up on this.


Hello All, I have had to edit this as it has been marked as a duplicate. Please note I am aware of the rule of three / five and have alluded to know that I need to include this above. My actually questions in the bullet points are around how the vector handles and object being pushed.

Does this create a temporary object and runs it constructor then performs a copy of this temporary object into the vector. Or conversely does it create the object straight into the vector.

Also as commented below it would seem that emplacing the object into the vector will avoid the need for a copy function as it seems to create the object directly. I am aware I will still need to implement the rule I am just trying to understand what the standardised code is doing and how it works.

  • Do you have `arr` as your class atribute? Or do you refer to `grid`? – ClaudiusDan Jul 09 '18 at 09:45
  • 6
    Since you aren't averse to using the standard library, how about ditching `new` and `delete` in favor of `std::vector`? This entirely prevents your problem. Zero is the preferred option in the [rule of three/five/zero](https://en.cppreference.com/w/cpp/language/rule_of_three). – StoryTeller - Unslander Monica Jul 09 '18 at 09:45
  • @ClaudiusDan Sorry I have updated, this was an issue when I was generalising my code for readability.. – user10052900 Jul 09 '18 at 09:48
  • @StoryTeller The reason I was avoiding using a vector for the `arr` is that once initialised this will be a fixed size so I will not need the overhead of the vector class to run this. – user10052900 Jul 09 '18 at 09:49
  • 1
    you can use `objVector.emplace_back(5);` instead of the `push_back`, this will create the object in place. – mch Jul 09 '18 at 09:50
  • 3
    @user10052900 - What overhead? You are engaging in premature optimization – StoryTeller - Unslander Monica Jul 09 '18 at 09:51
  • 2
    You still have `std::unique_ptr` in that case. – Jarod42 Jul 09 '18 at 09:52
  • @mch - I will be honest and say when reading on the topic I have only come across `push_back`. When using `emplace_back` will this avoid the need for copy constructors ect. and just create the object into the vector? – user10052900 Jul 09 '18 at 09:52
  • Yes, that's the difference between the 2 methods, `push_back` requires an object and copy it into the vector and `emplace_back` requires the constructor parameters and creates the object in the vector. – mch Jul 09 '18 at 09:55
  • Just because `new[]` is *more dangerous* than `vector` does not mean that it is faster. – Caleth Jul 09 '18 at 09:56
  • @mch - Thanks very much I will try this as it sounds ideal. – user10052900 Jul 09 '18 at 10:00
  • @Caleth & others - I would appreciate if you could point me towards somewhere that could teach me more about the issues of `new[]` vs vectors, as I would like to read up more. The reason I went down this route is that this array is set up once during object initialisation and after this the size will never change. The array is then Deleted at the end of the objects lifetime. I am not trying to disagree with the consensus I am just wanting a bit more information as it is hard to bridge the gaps between our thought processes. – user10052900 Jul 09 '18 at 10:03
  • If you have a vector, and never add or remove elements, it is *almost exactly* "correctly handled `new[]` and `delete[]`". – Caleth Jul 09 '18 at 10:26
  • 1
    Possible duplicate of [What is The Rule of Three?](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) And https://stackoverflow.com/questions/4782757/rule-of-three-becomes-rule-of-five-with-c11 – Richard Critten Jul 09 '18 at 10:43
  • @RichardCritten I have elaborated my question – user10052900 Jul 09 '18 at 10:55
  • 1
    Unless you follow the rules for making the move and assignment operators operator `noexcept`, then when std::vector has to resize itself it will copy the elements (to provide the strong exception guarantee). As you have not followed the rule of 3 (or 5) you will get double deletion / heap corruption. – Richard Critten Jul 09 '18 at 11:04
  • @RichardCritten - As I have said in my question and edit I know I have to include the rule and I will be I am just asking about how a vector handles the objects. Although your point around when an vector resizes it creates copies of the members is a very good point that I was not aware of, thanks. These are the types of insights that I am after. – user10052900 Jul 09 '18 at 11:11

1 Answers1

1

You need a copy/move constructor and to retain the size of the array. See the famous Rule of 3/5

class obj {
public:
    int* arr;
    const size_t size;
    obj(const obj& other)
        :size(other.size)
    {
        arr = new int[size];
        std::copy(other.arr,other.arr+size,arr);
    }
    obj& operator=(const obj&) = delete; // unclear what to do if size!=other.size
    obj& operator=(obj&&) = delete; // unclear what to do if size!=other.size
    obj(obj&& other)
        :size(other.size)
    {
        arr = other.arr;
        other.arr=nullptr;
    }
    obj(size_t x) 
        :size(x)
    {
        assert(x>0);
        arr = new int[x];
    }
    ~obj() {
        if(arr)
            delete[] arr;
    }
    // All functionality stripped for clarity
};

P.S. No need to be so dogmatic on smart pointers. They help but we survived 30 years without them.

IlBeldus
  • 1,040
  • 6
  • 14
  • Note that the check for non-null arr is not needed in the destructor. `delete[]` will check for a null pointer itself before trying to deallocate. https://en.cppreference.com/w/cpp/language/delete – Paul Belanger Jul 09 '18 at 13:48