4

I had a midterm in c++ and one of our assignments was to write a class, that overloads the << operator so that we could print private fields of that class to std::ostream, like so:

Crate c1(2600, 9500);
Crate c2;
cout << c1 << ", " << c2 << endl;

had to print Crate weight: 2600 Crate value: 9500, Crate weight: 0 Crate value: 0

My implementation of this was

class Crate {
    int weight;
    int value;
public:
    Crate ():weight(0), value(0) {}
    Crate (int w, int v);
    Crate& operator+= (Crate& c);
    int operator+= (int& w);

    friend ostream& operator<< (ostream& out, Crate c) {
        out << "Crate weight: " << c.weight;
        out << " Crate value: " << c.value;
        return out;
    }
};

However, I got 0/3 points for the overloaded << operator, and the only difference between my solution and the evaluation template was that I pass Crate c by value and they passed it by reference.

Of course, if the class would have dynamically allocated member variables, I would have to pass instances of it by reference, but this is not the case here.

I know that the example provided at cppreference.com uses a const reference, but is there any strict rule that one MUST use a const reference when doing this?

I understand that my solution copies both weight and value to the stack instead of only copying a pointer to the object, but is +4 bytes (in case of a 32 bit system) on the stack really that much of a problem, or am I missing something much more serious (like a memory leak of some sort)?

I also checked the disassembly of both solutions, but I couldn't find anything that would make my solution worth zero points.

I have also asked my professors but they started to talk about passing it as const but I don't see how that would make this solution any better in terms of works / doesn't work. An other professor told me that "this is syntactically completely incorrect." however it compiled with Visual Studio and MinGW, so I can't make any sense of that either.

Please also note that this is a course for electrical engineers.

19greg96
  • 2,592
  • 5
  • 41
  • 55
  • I don't know if I agree with it being worth 0, but the rule of thumb I've always heard is if the type is larger than it's pointer (basically any non-primitive), pass by reference, and it should always be const unless you absolutely need the ability to modify it. – Carcigenicate Apr 01 '16 at 18:31
  • @Carcigenicate `struct Cls { int i; };` => `sizeof(Cls) < sizeof(Cls*)` on typical 64 bit platforms – Ryan Haining Apr 01 '16 at 18:38
  • I known that operator overloading requires the iterating parameter to be const. However, these types of technicalities are beyond my knowledge. Here is an explanation that might help: http://stackoverflow.com/a/2828320/4775223 – Wilmer SH Apr 01 '16 at 18:39
  • @RyanHaining Well, obviously if your class only has a single primitive field it's going to be pretty tiny. Saying "basically any non-primitive" was a little broad on my part. – Carcigenicate Apr 01 '16 at 18:40
  • FYI your body can just be `return out << "Crate weight: " << c.weight << " Crate value: " << c.value;` – Ryan Haining Apr 01 '16 at 18:41
  • @RyanHaining I know, but we had to write this on paper, and I was running out of space, so I decided to break it into two lines, and I wanted to reproduce my written code here as closely as possible. – 19greg96 Apr 01 '16 at 18:44
  • @Carcigenicate or `struct {short a; short b; short c;}` or `struct {char arr[4];}` or `union {float f; int i; char a[4]; short s[2]};` Or any class with no data members, etc. For PODs it happens all the time. – Ryan Haining Apr 01 '16 at 18:45
  • 4
    By the way, Greg, keep up the attitude and you might turn out a very fine programmer. This is exactly what I want to see in the new generation - attention, questioning authorities, indepdendent thinking and reasoning. – SergeyA Apr 01 '16 at 18:48
  • The two `operator+=`s should take their arguments by value or by `const&`; by value would be consistent with the stream inserter. I have no problem with either value or `const&` for all three here. – Pete Becker Apr 01 '16 at 20:29
  • Amending my previous comment, the integer argument should be passed by value. Either one is fine for the `Crate` arguments. – Pete Becker Apr 01 '16 at 20:37

3 Answers3

5

Is it obligatory to pass classes by reference in c++

No.

"Passing it as const" that's just silly, if you're passing by value then it doesn't matter to the outside world if you mark the parameter as const.

To answer your question as directly as possible, it is not wrong to take the other class by value. When the objects are smaller it's better to pass by value. In your case the size difference is 0 on a typical 64-bit platform (2 * 4bytes per int vs one 8 byte pointer). So your "+4 bytes on the stack" actually doesn't exist, it's the same size

I'd predict an optimizing compiler to rewrite your code to take the Crate by value, even if you declared it as taking by reference. This would be for spatial locality reasons and ease of access. If a pointer is passed, you'd need to follow the pointer to get to the data members, which adds a step.

Your professor is wrong to give you 0 points for this, unless there was some requirement made very clear in the course that all operators must take reference parameters. The const Crate vs Crate issue doesn't matter.

Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
4

You should not have received a 0 but you should not have received full credit either.

friend ostream& operator<< (ostream& out, Crate c) 

Is going to create a copy every time you call it which can be inefficient. To avoid those copies you want to pass by reference like

friend ostream& operator<< (ostream& out, Crate& c)

But this introduces a new problem that your first version didn't have as to pass by reference you need to supply an lvalue to the function. You would not be able to use it like

std::cout << Crate();

To combat that we can take the Crate by const & which will avoid copies and allow us to bind to temporaries

friend ostream& operator<< (ostream& out, const Crate& c)
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
3

Studying is usually about good habits and general knowledge, much less of a petty examples you are writing.

Yes, in your case, performance-wise passing by value is not going to change anything. But there are two issues with that:

  • Habit. You should make a habit of knowing how to pass your arguments. If modification is not required and data should not be moved, pass by const reference;
  • The way you wrote it, your class has to be copy-constructible. Yours is, but it's not always the case.

As for the actual zero as a mark - don't sweat over it. Means nothing.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • 2
    Actually it was probably useful. Ten bucks says OP learned more than most of his classmates over this by digging in and examining the code right down to the compiled output. Ripped off on their grade, yes, but long-term beneficial. – user4581301 Apr 01 '16 at 18:54