-1

I have this C++ class that accepts a day number of the year and converts it to the month-day format. It works fine but my problem with it is that my overloaded post-increment operator doesn't work as expected.

I understood that the following code...

int x = 10;
std::cout << "x: " << x << "\n";
std::cout << "x++: " << x++ << "\n"; // Gives x, then increments.
std::cout << "++x: " << ++x << "\n\n"; // Increments, then gives x.

... prints the following:

x: 10
x++: 10
++x: 12

So, the post-increment first passes the object to << then increments it. While the pre-increment adds one to the object, then passes it to <<.

However, in the code below, the overloaded post-increment method works the same as the pre-increment method -- which, as I've understood, isn't supposed to happen. What's happening here?

Three files: (1)main.cpp (2)DayOfYearMod.h,(3)DayOfYearMod.cpp

main.cpp

#include <iostream>
#include "DayOfYearMod.h"

int main() {
    DayOfYearMod in(1); // Day 1 for January 1.

    std::cout << "Test 1. Inline: \n";
    std::cout << " ... in: " << in << "\n";
    // Next. Supposed to pass Jan. 1 to << but passed Jan. 2
    std::cout << " ... in++: " << in++ << "\n";
    std::cout << " ... in: " << in << "\n";
    // Next. Pass Jan. 2 + 1 to <<. Works as expected.
    std::cout << " ... ++in: " << ++in << "\n"; // Day 3. Got Day 3.
    std::cout << " ... in: " << in << "\n\n";

    DayOfYearMod d_pre(31), d_post(31); // Day 31 for January 31

    std::cout << "Test 2. Individual Objects. \n";
    std::cout << " ... d_pre: " << d_pre << "\n";
    std::cout << " ... ++d_pre: " << ++d_pre << "\n";
    std::cout << " ... d_post: " << d_post << "\n";
    std::cout << " ... d_post++: " << d_post++ << "\n"; // This is supposed to be Jan. 31
    std::cout << "\n";

    return 0;
}

DayOfYearMod.h

#include <string>
#include <iostream>

class DayOfYearMod {

public:
    static const int dayMax;
    static const int numMonths;
    static int daysPerMonth[ ]; // One-based indexing. 1 = January
    static std::string monthNames[ ];

private:
    int numDay; // -nth day of the year
    // Results
    int numMonth;
    std::string month;
    int dayOfMonth;
    void extractDetails();

public:
    DayOfYearMod();
    DayOfYearMod(int);
    void setDay(const int);
    bool isInRange(int);
    void addToDay(int);
    void print();
    // Overloaded Operators
    DayOfYearMod operator++(int);
    DayOfYearMod& operator++();
    friend std::ostream& operator<<(std::ostream&, const DayOfYearMod&);
};

DayOfYearMod.cpp

#include <iostream>
#include "DayOfYearMod.h"

const int DayOfYearMod::dayMax = 365;
const int DayOfYearMod::numMonths = 12;

// One-based indexing. January = 1.
// Assuming no leap years.
int DayOfYearMod::daysPerMonth[DayOfYearMod::numMonths + 1] = {
        0,
        31, 28, 31, 30, 31, 30,
        31, 31, 30, 31, 30, 31
};

std::string DayOfYearMod::monthNames[DayOfYearMod::numMonths + 1] = {
        "",
        "January", "February", "March",
        "April", "May", "June",
        "July", "August", "September",
        "October", "November", "December"
};

// Constructors
DayOfYearMod::DayOfYearMod(int num) {
    numDay = 0;
    addToDay(num); // This function allows for out-of-range values.
}

// Default constructor delegated
DayOfYearMod::DayOfYearMod() : DayOfYearMod(1) { }

// PRIVATE METHODS
void DayOfYearMod::extractDetails() {
    int temp = numDay;
    for (int i = 1; i <= numMonths; i++) {
        temp = temp - daysPerMonth[i];
        if (temp <= 0) {
            numMonth = i;
            month = monthNames[i];
            dayOfMonth = temp + daysPerMonth[i];
            break;
        }
    }
}

void DayOfYearMod::addToDay(int plus) {
    this->numDay = (this->numDay + plus) % DayOfYearMod::dayMax;
    // If r == 0, then the day is the max day.
    if (this->numDay == 0) this->numDay = DayOfYearMod::dayMax;
    this->extractDetails();
}

// PUBLIC METHODS
bool DayOfYearMod::isInRange(int val) {
    if (val > 0 && val <= dayMax) {
        return true;
    } else return false;
}

void DayOfYearMod::setDay(const int val) {
    if (!isInRange(val)) {
        std::cout << "Day not in range.";
        exit(-1);
    }
    numDay = val;
    extractDetails();
}

void DayOfYearMod::print() {
    std::cout << month << " " << dayOfMonth;
}

// OVERLOADED OPERATORS
// Prefix Increment
DayOfYearMod& DayOfYearMod::operator++() {
    this->addToDay(1);
    return *this;
}

// Postfix Increment
DayOfYearMod DayOfYearMod::operator++(int) {
    this->addToDay(1);
    return *this;
}

// Output stream operator
std::ostream& operator<<(std::ostream& out, const DayOfYearMod& d) {
    out << d.month << " " << d.dayOfMonth;
    return out;
}

Running the code gives me this...

Test 1. Inline: 
 ... in: January 1
 ... in++: January 2
 ... in: January 2
 ... ++in: January 3
 ... in: January 3

Test 2. Individual Objects. 
 ... d_pre: January 31
 ... ++d_pre: February 1
 ... d_post: January 31
 ... d_post++: February 1
Vishaal Shankar
  • 1,648
  • 14
  • 26
  • This is much more code than needed to demonstrate the problem. – aschepler Feb 09 '18 at 05:58
  • @NickyC: I'm amazed that I haven't found this with all the hours I've spent trying to understand this problem. Thanks for great reference! – TheLoneWoof Feb 09 '18 at 06:04
  • @ascheple: I agree it is but I asked a question here a while ago and was asked to post the whole executable code. I'll try to do better next time. – TheLoneWoof Feb 09 '18 at 06:04
  • 1
    Possibly that question was not complete enough to compile? The best questions use a [mcve]: code that is both "minimal", meaning things irrelevant to the question have been removed, and "complete", needing no additions to compile and show the compiler error or unexpected behavior the question is about. Making one takes some effort, but it's polite to potential answerers, worth your own time to understand better, and in some cases you can even discover the issue yourself during the process. – aschepler Feb 09 '18 at 06:12
  • @TheLoneWoof : I *bet* you weren't asked to post your whole program. You were asked to post a [mcve]. Specifically, that means you must take your whole program, and remove all the bits that aren't necessary to show the problem. (In this case, for example, just print the day of the month, and remove the month-end or year-end wrapping code. Then use `5` as your initializer.) – Martin Bonner supports Monica Feb 09 '18 at 06:13
  • I can quote that guy saying "post everything" when I just wanted to clarify a comment my previous instructor made about my C++ method looking a lot like Java. – TheLoneWoof Feb 09 '18 at 06:16
  • Should I edit my question here still though? It's solved already and it's also marked as a duplicate. – TheLoneWoof Feb 09 '18 at 06:17

1 Answers1

2
// OVERLOADED OPERATORS
// Prefix Increment
DayOfYearMod& DayOfYearMod::operator++() {
    this->addToDay(1);
    return *this;
}

// Postfix Increment
DayOfYearMod DayOfYearMod::operator++(int) {
    this->addToDay(1);
    return *this;
}

Both of these functions first increment the object, and then return the new value. It's true your postincrement operator returns by value rather than by reference (as it should), but the returned copy contains the incremented value, not the value before increment like we'd expect.

A usual postincrement implementation looks more like this:

// Postfix Increment
DayOfYearMod DayOfYearMod::operator++(int) {
    DayOfYearMod copy(*this);
    this->addToDay(1);
    return copy;
}

(By the way, you don't need to write this-> before member names. But if you knew that and choose to do it anyway as a matter of style, okay.)

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • I should have known I had to make the copy explicit. I thought it was implied in the post-increment operator but I overloaded it, so. Thanks for taking the time to help me! – TheLoneWoof Feb 09 '18 at 06:06