0

I have a class with two vectors: an int and an Str. Now I want to define a copy constructor so that the order of elements is reversed; e.g. if a=(1,Hello),(2,World) and I write auto b=a; I get b=(2,world),(1,hello). Which is working perfectly. The issue I am having is overloading the = operator so that the copy constructor is utilized. Here is my Class plus copy constructor:

class grade
{
  private:
    static int copies;
    std::vector<int> i;
    std::vector<std::string> s;

  public:
    grade() {copies++;};
    grade (int , std::string );
    void printval();
    void adder(int , std::string );
    int getcount();

    grade(grade & obj)
    {
        std::vector<std::string>::reverse_iterator strIt = obj.s.rbegin();
        for (std::vector<int>::reverse_iterator numIt=obj.i.rbegin();
             numIt!=obj.i.rend(); ++numIt)
        {
            this->adder(*numIt, *strIt);
            strIt++;
        }

    }
    grade operator =(grade );
};

When I overload the = operator the constructor is called and the trouble is that the valur is not passed to the LHS variable. Here is the overloaded =.

grade grade::operator=(grade cpy)
{
    grade newer = cpy; //Calls the copy constructor as expected
    return cpy;        //No error but blank value is returned to the LHS variable.
}

My main function is :

int main()
{
    grade c2(1,"Hello");
    grade c1;

    c2.adder(4,"World");
    c2.printval();
    std::cout<<std::endl;
    grade c3 = c2;
    c3.printval();
    std::cout<<std::endl;
    c1 = c2;
    std::cout<<std::endl;
    c1.printval();

    return 0;
}

Why does c1 remain blank??

gpoo
  • 8,408
  • 3
  • 38
  • 53

4 Answers4

3

The easiest way to implement the assigment operator is the copy and swap idiom:

grade& operator=( grade other )
{
   swap( *this , other );
   return *this;
}

Where swap is a function which takes two grades and swaps its values:

void swap( grade& lhs , grade& rhs )
{
    using std::swap;

    swap( lhs.first_member  , rhs.first_member  );
    swap( lhs.second_member , rhs.second_member );
    swap( lhs.third_member  , rhs.third_member  );
    ...
}

Note that swap() is an overload/specialization of std::swap(). std::swap() uses the assigment operator to perform the swaping by default, so if you use it you lead into infinite recursion. What you have to do is to write your own swap overload which performs member by member swapping (As in the example above).

Note that you should use unqualified swap() to not disable ADL, and let the compiler find the custom swap() overload of the members (If they provide one) or the Standard Library version instead.

Community
  • 1
  • 1
Manu343726
  • 13,969
  • 4
  • 40
  • 75
  • `swap` will call `operator=`, causing infinite recursion. You need to swap the individual member variables. Either that or implement an overload of `swap()` for your class. – Adam H. Peterson Feb 06 '14 at 21:39
  • 1
    @AdamH.Peterson the idea is you overload `swap` for your own type. – juanchopanza Feb 06 '14 at 21:40
  • Ah, I see you've added the overload to your answer. (But you probably should demonstrate the memberwise swap inside your example `swap()`.) – Adam H. Peterson Feb 06 '14 at 21:41
  • Your use of qualified `std::swap` will disable ADL, which means if `first_member` (etc.) implement their own overloads, they will not be considered. You should put `using std::swap;` in the function and then call `swap()` unqualified. – Adam H. Peterson Feb 06 '14 at 21:44
  • 1
    @AdamH.Peterson I always forget the ADL issue, thanks – Manu343726 Feb 06 '14 at 21:45
1

What you want is the copy-and-swap idiom:

#include <algorithm>
grade &grade::operator=( grade cpy ) {
    using std::swap;
    swap( i, cpy.i );
    swap( s, cpy.s );
    return *this;
}
Adam H. Peterson
  • 4,511
  • 20
  • 28
1

You asked: Why does c1 remain blank??

The answer is that the assignment operator does not modify the object itself. Here's your code:

grade grade::operator=(grade cpy)
{
  grade newer=cpy; //Calls the copy constructor as expected
  return cpy; //No error but blank value is returned to the LHS variable.
}

The method returns a copy of the input parameter cpy but does not modify the current instance. When this method is executing, *this is the "LHS variable", and it's not being modified at all. Within this method (operator overload, you need to actually modify the member variables of the current instance!

aldo
  • 2,927
  • 21
  • 36
  • I hope that answers your question. For advice on how to actually go about modifying the member variables in the current instance, refer to any one of the other answers that explain the "copy and swap" approach. :-) – aldo Feb 06 '14 at 21:53
  • The method does not return nothing because its locked at the recursive call to operator=. – Manu343726 Feb 06 '14 at 21:58
  • @Manu343726 No, this one isn't. – juanchopanza Feb 06 '14 at 21:59
  • @juanchopanza uups, `newer`, not `*this`... Sorry – Manu343726 Feb 06 '14 at 22:00
  • Well you are right but If I try to modify `*this` I get an infinite loop.Looks like swap is the only way. –  Feb 06 '14 at 23:02
  • Thanks @Manu343726 and @aldo ... it was an infinite call on `=`. –  Feb 07 '14 at 11:21
  • Great! Also, please note the other (subtle but important) change recommended in the other answers: operator= usually returns a reference to the object; note that in all the examples the operator is declared `grade& operator=( grade other )` and ends with `return *this;`. Your code returns an object by value, which can lead to mysterious bugs later on. – aldo Feb 07 '14 at 15:27
0

Change your copy constructor and your assignment operator as follows:

grade::grade(const grade& cpy)
{
    *this = cpy;
}

grade& grade::operator=(const grade& cpy)
{
    if (this == &cpy)
        return *this;

    std::vector<std::string>::reverse_iterator strIt = cpy.s.rbegin();
    for (std::vector<int>::reverse_iterator numIt=cpy.i.rbegin();numIt!=cpy.i.rend();++numIt)
    {
        this->adder(*numIt, *strIt);
        strIt++;
    }

    return *this;
}

And as a general scheme:

object::object(const object& input)
{
    *this = input;
}

object& object::operator=(const object& input)
{
    if (this == &input)
        return *this;

    Deallocate();
    Allocate(input);
    Initialize(input);

    return *this;
}
barak manos
  • 29,648
  • 10
  • 62
  • 114
  • 2
    In my opinion (and I believe in the general C++ community), it is much better to implement assigment in terms of copy (using the copy-and-swap idiom) than to implement copy in terms of assignment. – Adam H. Peterson Feb 06 '14 at 21:51
  • I have always considered the self assigment check an stupid and unneccesary thing. Consider when you have written a sentence like `a = a` for last time.... – Manu343726 Feb 06 '14 at 21:51
  • @Manu343726: Possibly. But if the user does something like that, and memory allocation is involved (`delete/new`), then a runtime error will occur. – barak manos Feb 06 '14 at 21:53
  • @Manu343726: I would tend to agree. Usually if a self-assignment check is necessary for correctness, there is an exception safety problem with the code. If it's an optimization, realize that self-assignment is generally pretty rare, and the self-assignment check will probably not buy you much, or perhaps even cost you. – Adam H. Peterson Feb 06 '14 at 21:54
  • @barakmanos: If you need self-assignment checks for correctness, I recommend you review and refactor your code until you don't. Most of the time, the original code is wrong anyway, because it will have an exception safety problem. – Adam H. Peterson Feb 06 '14 at 21:55
  • 1
    No, because what you have provided as a *general scheme* is not a good general scheme at all. The good scheme is to use RAII-based resources, not to manage resources by hand, letting (In almost all cases) the compiler generate a working assigment operator, copy ctor, and dtor (That is, the so named Rule of Zero). In the few cases where RAII aggregation not leads to working default ctors/operator=, is where the copy and swap kicks in. – Manu343726 Feb 06 '14 at 21:55
  • @Adam H. Peterson, thanks for the opinion. I copied that answer from a `BigNum` class that I have implemented a long time ago (replaced the names of course). Back then, I found this scheme to be the best one in terms of code reuse. – barak manos Feb 06 '14 at 21:55
  • Regarding the "safety check": 1. It is merely an address comparison, so shouldn't "cost too much" in general (although, that would depend on the rest of the code that is using it, of course). 2. If you provide this class as a general utility (open source, library, etc), then you cannot guess what the user will do, hence you would want to make sure that this case is handled in a safe manner. – barak manos Feb 06 '14 at 22:02