Bad alloc exception from copy cons?

Pages: 12
May 23, 2018 at 4:48pm
(I double posted, see the post on the previous page as well. Whoops.)

but lets say an object had another object as a member variable(which also called swap() in its operator= function) as in composition,that means we couldn't use the std::swap() function? because std::swap would call the assignment operator on that object right?

If a swappable type in the C++ standard library exists, the standard library should either use std::swap correctly, or a specialization of std::swap. I think. (A class can also be not assignable on purpose, and therefore not swappable by std::swap, and it will be a compilation error, but still not infinite recursion.)

If, within your class, you have another custom class object that uses its own swap in its implementation of its assignment operator, that type will have to swap each component of that custom class. Your class shouldn't have to worry about how that class implements its assignment operator or swap function if you use the "using std::swap;" construct and that custom class follows the guidelines in http://en.cppreference.com/w/cpp/concept/Swappable

any standard library functions (for example, many algorithms) expect their arguments to satisfy Swappable, which means that any time the standard library performs a swap, it uses the equivalent of using std::swap; swap(t, u);.

Typical implementations either

1) Define a non-member swap in the enclosing namespace, which may forward to a member swap if access to non-public data members is required
2) Define a friend function in-class (this approach hides the class-specific swap from name lookup other than ADL)


I think it would be best to just try making what you described, and you'll see what works and what goes awry. And if some specific breaks and you're not sure why, ask away and I or someone that's an expert in assignments/swapping can explain why :)

(The alternative is to also not use swap to implement operator=. Sometimes it's cleaner, other times not).
Last edited on May 23, 2018 at 4:55pm
May 23, 2018 at 6:57pm
yup crash!

that is a pretty nasty bug,I mean its something that many people could easily do what it out realising it.

also I never knew a move assignment operator even existed until Icys post :O

If, within your class, you have another custom class object that uses its own swap in its implementation of its assignment operator, that type will have to swap each component of that custom class.


good point,never even thought of that it may be a bit tedious though

thanks guys :) I don't think I have any more questions for now :)
Last edited on May 23, 2018 at 7:03pm
May 23, 2018 at 7:24pm
Just to clarify I will try keep it as brief as possible from what I now understand

in the example where we use the assignment operator a = b;

first the operator= will be called inside this std::swap(a,b) will be called inside std::swap(a,b) the operator= will be cause because person temp = a; a = b; b = temp;

because temp = a uses the operator= from the person object persons operator= will again again call swap which then calls std::swap std::swap then calls swap again and this pattern keeps on going causing infinite recursion right?

but to avoid this we can make an object non copyable by making the operator= function private or even protected.


thanks
May 23, 2018 at 8:09pm
In the buggy recursive example (on the end of page 1), std::swap is never called. You are doing a simple tail-end recursion of your swap function.

If you specifically wrote std::swap(a, b); instead of using std::swap; swap(a,b);, then your explanation would be correct pattern (operator= calls std::swap, calls operator=, calls std::swap, ...) keeps on going). Edit: I'm wrong, it seems to be calling std::swap either way. Either way, infinite recursion though.

Making an object non-copyable is an extreme "solution", the key here is to make sure you don't implement Person::operator=(Person) by using std::swap(Person&, Person&). Usually, things are made non-copyable for other reasons, to prevent abuse of functionality, for example std::mutex is non-copyable.
Last edited on May 23, 2018 at 8:53pm
May 23, 2018 at 8:18pm

If you specifically wrote std::swap(a, b); instead of using std::swap; swap(a,b);, then your explanation would be correct pattern (operator= calls std::swap, calls operator=, calls std::swap, ...) keeps on going). Either way, infinite recursion though.


but when I call swap() with each persons member variables,it actually works

the code below prints hii and also when I tried to print the name jim it printed the name jim,so everything seems to be swapped correctly? maybe it's something my compiler is doing?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <stdio.h>
#include <iostream>
#include <cstring>

using namespace std;

class person{

     public:

     string name;
     int age;
     char* cstr;
     int len;

     person(string n,int a,const char* c)
     {
         name = n;
         len = strlen(c)+1;
         age = a;
         cstr = new char[len];
         memcpy(cstr,c,len);
     }
     person(){

     }

     person(const person &other){

         name = other.name;
         age = other.age;
         len = other.len;
         cstr = new char[len];
         memcpy(cstr,other.cstr,len);
     }
     person(person &&other){

         name = other.name;
         age = other.age;
         len = other.len;
         cstr = other.cstr;
         other.cstr = nullptr;
     }

     person& operator=(person other){

        swap(*this,other);
        return *this;

     }

     void swap(person& one,person &two){

        using std::swap;
        swap(one.name,two.name);
        swap(one.age,two.age);
        swap(one.len,two.len);
        swap(one.cstr,two.cstr);
     }

     ~person(){

        delete[] cstr;
     }
};

int main()
{
  person a("adam",26,"hello");
  cout << a.len << endl;
  person b = a;
  cout << b.name << endl;
  person c = a;
  cout << c.name << endl;
  c = b;  // Cop=(b);
  cout << "try now :: " << c.name << endl;
  cout << "try again :: " << c.cstr << endl;
  c = person("jim",37,"hii");

  cout << c.cstr << endl; // prints hi
  cout << c.name << endl; // prints jim
}

Last edited on May 23, 2018 at 8:19pm
May 23, 2018 at 8:45pm
Your swap looks correct there. That is indeed calling std::swap(std::string&, std::string), std::swap(int&, int&), std::swap(int&, int&) and std::swap(char*&, char*&). You're swapping each component, so everything is peachy.

But I was also slightly wrong, your example at the end of page one does actually still call std::swap, assignment operator, swap, std::swap, assignment operator...
1
2
3
4
5
6
7
8
9
10
11
This will infinitely recurse until a crash:
Person assignment
  Person swap
   std::swap
     Person assignment
       Person swap
         std::swap
           Person assignment
             Person swap
               std::swap
                 Person assignment


So ignore the parts of the conversation where I said it was simply tail-end recursion and not cyclic recursion. I thought it would prefer the explicit version before the template version, but my hypothesis was wrong. Everything else I said (the main point, that it recurses) is still correct, though.

Last edited on May 23, 2018 at 8:47pm
May 23, 2018 at 8:47pm
thanks Ganado much appreciated I know have a decent understanding of this topic :)

So ignore the parts of the conversation where I said it was simply tail-end recursion and not cyclic recursion. I thought it would prefer the explicit version before the template version, but my hypothesis was wrong. Everything else I said (the main point) is still correct, though.


probably depends on what compiler you are using
May 23, 2018 at 8:49pm
Last edited on May 23, 2018 at 8:52pm
May 23, 2018 at 9:00pm
may have been a different story if I used a different compiler,

my compiler is mingGW and running on a 64 bit windows 10 machine using codeblocks

thanks for all the help you are awesome!!! :)
May 24, 2018 at 1:10pm
I think we're talking about two different things. Like I said, the overload resolution of which function it chooses is precisely defined in the standard, I just don't exactly understand how it makes the decision. If one compiler does something different when it comes to overload resolution, it is wrong. But that's not what's happening here, your compiler is not wrong. Maybe I'll ask it as a question in another thread.
Last edited on May 24, 2018 at 1:11pm
May 24, 2018 at 3:13pm
good idea :)

could you post the link to it in this thread

thanks
Topic archived. No new replies allowed.
Pages: 12