1

I have a class, Somec, with a vector of pointers to items. Each item in the vector is from another class type, Myitem.

I want to make a copy function for Somec, but I am having some problems.

Also, I have a copy function for Myitem, do I have to use it in the copy function for Somec?

What I have tried so far:

class Somec {
    string Name;
    std::vector<Myitem*> Items;

public:
    Somec(const Somec& somec); // my copy function
};

/// this the clas to my item :
class Myitem {
    Itemk itemk;
public:
    // ... some fuction 
};

// now I want to make a copy function for Somec

Somec::Somec(const Somec& somec) {
    int size = somec.Items.size();
    this->Items = new (vector*) (size); // i get an error here
    this->Name = somec.Name;
    for(int i = 0; i < size; i++) {
        *Items.at(i) = *somec.Items.at(i);
    }
}

UPDATE: I made the copy function just like Remy Lebeau told me to, but when I try to test this function the code stops working. This is how I am testing it:

class Somec {
    string Name;
    std::vector<Myitem*> Items;

public:    
    Somec(const Somec& somec); // my copy function
};

Somec::Somec(const Somec& somec) { // new copy function for somec
    int size = somec.Items.size();
    this->Name = somec.Name;
    for(int i = 0; i < size; i++) {
        Items.push_back(new Myitem(*somec.Items[i]));
    }
}

// create item function
void Somec::createitem(char* name, const int& Time, const int& level) {
    try{
        Myitem* ORitem=new Myitem(name, Time, level);
        Somec::Items.push_back(ORitem);        
    }
    catch(MemoryProblemException& error) {
        throw SomecMemoryProblemException();
    }
}

std::vector<Myitem*> Somec::getAllItems() const {
    return Items;
}

/// this the class to my item :
class Myitem {
    Itemk itemk;
public:
    // ... some fuction 
};

// my copy function to MYitem 
Myitem::Myitem(const Myitem& item){
    Myitem* currie = Myitem::clone_item();
    curritem->item = itemCopy(item.item);
    if (!curritem->item) {
        throw itemMemoryProblemException();
        return;
    }
}

void testCopy() {
    Somec somec("name1")
    somec.createitem((char*) "item1", 30, 1, 2);
    Somec temp(somec);
    int x=0;
    if ( std::equal(somec.getAllItems().begin() + 1, somec.getAllItems().end(), somec.getAllItems().begin()) ) {
        x++;
    }
    ASSERT_TRUE(x==1);
}

What is my problem? I mean, I did the copy function, I think it is true. But why does the code stop working? Am I testing this in the right way?

My createitem function, I am 100% sure about it, actually.

I am just trying to add items to the the vector in Somec and check if this happened correctly. I learned that in C++, we need a copy constructor, so I wrote one, and thought about testing it since this is my first time doing one.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
jena90
  • 41
  • 7
  • The correct term for what you are calling a copy function is a [copy constructor](http://en.cppreference.com/w/cpp/language/copy_constructor). – François Andrieux Jun 29 '17 at 17:29
  • 2
    If you really need pointers I suggest you use `std::unique_ptr` and [this method](https://stackoverflow.com/questions/34797849/unique-copy-of-vectorunique-ptr) – NathanOliver Jun 29 '17 at 17:30
  • In short: Don't use raw pointers at all. – πάντα ῥεῖ Jun 29 '17 at 17:30
  • why ? i mean this victor is a victor of pointers and i need to copy it to the new somec – jena90 Jun 29 '17 at 17:31
  • 3
    `Name` is a `std::vector` of pointers, it is not a pointer itself. There is no reason to allocate one with `new`. You can also simply copy a `std::vector`. `this->Items = somec.Items;` should do the trick. Consider using an [member initializatier list](http://en.cppreference.com/w/cpp/language/initializer_list). Beware that you are copying pointers to `Myitem` which means multiple `Somec` will refer to the same instances of `Myitem`. This is a dangerous situation that is likely to lead to many many bugs in the future. – François Andrieux Jun 29 '17 at 17:31
  • i mean i learned that is i have a class with an array in it and i want to make a copy constructor to this class i would have to make a new array and fill it with loop – jena90 Jun 29 '17 at 17:33
  • Does `Somec` own the `MyItem*` that are contained in the vector? What I mean is -- does the destructor of `Somec` call `delete` on each element in the vector? If so, what do you expect to happen if you copy `Somec`? – Chad Jun 29 '17 at 17:34
  • i think i am just really confused between copy and = ... – jena90 Jun 29 '17 at 17:46
  • @jena90 You are relying on "best practices" for C arrays. C arrays and standard containers are *very* different. You must not confuse one for the other. You are using an `std::vector` here, not an array. – François Andrieux Jun 29 '17 at 17:53
  • 1
    Google "the rule of zero" and follow it religiously. – n. m. could be an AI Jun 29 '17 at 18:28
  • wow why ?? there is an item already in the vector // because createitem adds an item to the vector :\ – jena90 Jun 29 '17 at 18:35

2 Answers2

1

If you want to copy elements from the vector of the other instance to the vector of your current instance in the copy constructor, then simply do this

class Somec {
    string Name;
    std::vector<Myitem> Items;
public:
    Somec(const Somec& somec); // my copy function
};

i.e. make the vector of values instead of pointers.

If for some reason you have to have to work with pointers, because maybe the copy constructor is deleted or something similar. Then still use this approach, but just don't copy the contents of in the copy constructor, let them get constructed and then assign them

Somec(const Somec& somec) {
    this->Items.resize(somec.Items.resize());
    for (std::size_t i = 0; i < somec.Items.size(); ++i) {
        this->Items[i] = somec.Items[i];
    }
    // or simply
    this->Items = somec.Items;
}
Curious
  • 20,870
  • 8
  • 61
  • 146
  • how is `this->Items[i] = somec.Items[i];` different from `this->items[i] = &(*(somec.items[i]))` ? just Curious – PYA Jun 29 '17 at 17:36
  • 1
    Can't you just to `this->Items = somec.Items;`? – François Andrieux Jun 29 '17 at 17:38
  • i think i am confused between copy and = operator ! what is the differance ? because i thought of doing the operator = like this and thought that the copy constructor needed new ! – jena90 Jun 29 '17 at 17:47
  • 1
    @FrançoisAndrieux just for demonstration purposes, since OP had similar code above – Curious Jun 29 '17 at 17:51
  • @jena90 I dont think I follow your question sorry, could you explain what you mean? – Curious Jun 29 '17 at 17:51
  • 1
    I think @jena90 wants to know the difference between the assignment operator and the copy constructor, am I right @jena90? – PYA Jun 29 '17 at 17:52
  • 1
    @pyjg the second one looks like it copies the address of the elements in the other vector to the current vector – Curious Jun 29 '17 at 17:52
  • 1
    @pyjg I changed it to be a vector of values, since OP was deep copying the vector manually. If it were pointers then he would shallow copy the pointed to things (in most cases) – Curious Jun 29 '17 at 17:55
  • oh in @jena90's answer it is a vector of pointers, you changed it to be a vector of objects, sorry. If it was a vector of pointers would my approach be appropriate? – PYA Jun 29 '17 at 17:55
  • 1
    @pyjg yeah that would work then, but if the other one is a vector of pointers, then you can save one round trip of dereferencing and just do `this->items[i] = somec.items[i]` – Curious Jun 29 '17 at 17:56
  • 1
    @jena90 I am slightly confused now. Does the problem require you to use pointers? – Curious Jun 29 '17 at 17:59
  • i stated that in the question. – jena90 Jun 29 '17 at 18:03
0

The default compiler-generated copy constructor already does exactly what your copy constructor is doing manually. So it is redundant code.

However, assuming Somec owns the items, then you do need to define a custom copy constructor, and it needs to allocate new Myitem objects and copy the existing data into them, eg:

Somec::Somec(const Somec& somec) {
    this->Name = somec.Name;
    std::size_t size = somec.Items.size();
    this->Items.reserve(size);
    for(std::size_t i = 0; i < size; i++) {
        Items.push_back(new Myitem(*somec.Items[i]));
    }
}

You will have to do something similar in an overriden operator= as well. You can utilize the copy constructor to help you:

Somec& Somec::operator=(const Somec& somec) {
    if (&somec != this) { 
        Somec temp(somec);
        std::swap(this->Name, temp.Name);
        std::swap(this->Items, temp.Items);
    }
    return *this;
}

And, of course, make sure you free the owned items in the destructor:

Somec::~Somec() {
    std::size_t size = somec.Items.size();
    for(std::size_t i = 0; i < size; i++) {
        delete Items[i];
    }
}

See Rule of Three for more details about why this trio is needed.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • i think i am confused between copy and = operator ! what is the differance ? because i thought of doing the operator = like this and thought that the copy constructor needed new ! – jena90 Jun 29 '17 at 17:48
  • mmm you made a new here whle the other guys saied no need .. which one is correct should i add new or not i am really confused – jena90 Jun 29 '17 at 18:02
  • @jena90 you are working with a vector of pointers. Your original copy code **shallow-copies** only the pointers themselves, so you end up with multiple `Somec` objects pointing to the same `Myitem` objects. If `Somec` owns the items, you will have multiple destructors freeing the same items, so you would need to **deep-copy** the `Myitem` objects using `new` in each copy. That is not needed if you use a vector of objects instead of a vector of pointers. The copy constructor and copy assignment operator have the same responsibility - make a copy and leave the two objects in safe conditions. – Remy Lebeau Jun 29 '17 at 19:30
  • @jena90 you say your test code is failing, but you did not show what `createitem()` and `getAllItems()` are actually doing. And why are you using `begin() + 1` instead of `begin()`? And why are you calling `getAllItems()` three times on the same `somec` object, and ignoring the `temp` object? You can't compare iterators from different container objects. You are misusing `std::equal` – Remy Lebeau Jun 29 '17 at 19:32
  • i actually think createitem is correct because i have tested it multipule times but i will insert it in here – jena90 Jun 29 '17 at 19:35
  • @jena90 just because you *think* it is correct doesn't *guarantee* that it actually is. If you want further help, you need to edit your question to provide a [mcve] and explain your expected results, so people can reproduce and verify your issues and explain what you are doing wrong. – Remy Lebeau Jun 29 '17 at 19:46
  • @jena90 `createitem` is allocating a new `Myitem` using `new`, so copies must be allocated with `new` as well, and the destructor must free them with `delete`. Just as I showed in my answer. As for your test code, you still haven't showed what `getAllItems` does, or explain what you are trying to compare for equality. Your use of `std::equal` is clearly wrong. At best, all it will do is compare pointers, which will never be equal the way you are using it. – Remy Lebeau Jun 29 '17 at 20:03
  • ohhhh so i was camparing them in a wrong way all this time !!!! ok i am gonna my question – jena90 Jun 29 '17 at 20:06
  • @jena90 also, `createitem` is ignoring memory errors like `std::bad_alloc`, which can be raised by `new` and `push_back`. – Remy Lebeau Jun 29 '17 at 20:10
  • @jena90 If you want to compare the *contents* of the `Myitem` objects themselves, you have to dereference the pointers in the vector. To do that with `std::equal`, you have to give it a predicate function that will dereference the pointers before then comparing the objects. – Remy Lebeau Jun 29 '17 at 20:11
  • @jena90 you need something more like this: `std::vector items = somec.getAllItems(); if (std::equal(items.begin(), items.end(), temp.getAllItems().begin(), compareItems) ) { x++; }` where `compareItems()` is like this: `bool compareItems(Myitem *item1, Myitem *item2) { return (*item1 == *item2); }` then you just need an `operator==` for `Myitem`. – Remy Lebeau Jun 29 '17 at 21:04