We're discussing constructors.
My problem is this:

Define a class called Month that is an ADT for a month with one variable as int to represent a month.

Include all of the following member functions: a constructor to set the month using the first three letters in the name of the month as three arguments, a constructor to set the month using an integer as an argument, a default constuctor, an input function that reads the month as an integer, an input funciton that reads the month as the first three letters in the name of the month, an output function that returns the next month as a value of type Month.

Here's what I've gotten so far:

class Month
{
public:
    Month ( char , char , char ) ;
    Month ( int ) ;
    Month ( ) ;

    void input ( char , char , char ) ;
    void input ( int ) ;
    void output ( char , char , char ) ;
    Month nextMon ( ) ;
private:
    int month ;
}

Could someone advise whether this is close or completely wrong (and why)?
Is this possible without having the three variables char1, char2, and char3 (or could I just implement those in main)?

ps) I'm lost as to how to do the output and the object returning functions

Recommended Answers

All 19 Replies

>a constructor to set the month using the first three letters in the name of
>the month as three arguments
That's weird.

>a constructor to set the month using an integer as an argument, a default constuctor
You can merge these guys with a default argument:

Month ( int init = 0 );

>an input function that reads the month as an integer, an input funciton
>that reads the month as the first three letters in the name of the month
These are weird too. I think the terminology is confusing because it sounds like the two member functions are setters, they don't actually take stream input.

>void output ( char , char , char ) ;
Where did this one come from?

>Is this possible without having the three variables char1, char2, and char3
I'd use a string, but if your program requires three characters, you don't have much choice.

>ps) I'm lost as to how to do the output and the object returning functions
Well, assuming the integer is the index of the month (0 = January, 1 = February, ... 11 = December), you can return a new Month object initialized with the next month:

Month::Month nextMon()
{
  if ( month == 11 ) {
    // Wrap around to January
    return Month ( 0 );
  }
  else
    return Month ( month + 1 );
}

>a constructor to set the month using an integer as an argument, a default constuctor
You can merge these guys with a default argument:

Month ( int init = 0 );

could you explain what init is?

>void output ( char , char , char ) ;
Where did this one come from?

oops, i left out part of the problem:

... an output function that outputs the month as an integer, an output function that outputs the month as the first three letters in the name of the month, and a member function that returns the next month as a value of type Month.

so i forgot void output ( int ) too

>could you explain what init is?
It's the integer parameter in Month ( int ) ; with a name and a default value.

>... an output function that outputs the month as an integer, blah blah blah
I think you need to get whomever wrote those requirements to give you a better idea of what "input" and "output" mean in this context.

I'm back.

Here's the portion that is giving errors:

Month::Month ( char c1 , char c2 , char c3 ) : setMbyNcheck ( c1 , c2 , c3 )
{
    c2 = tolower ( c2 ) ;
    c3 = tolower ( c3 ) ;

    switch ( c3 ) 
    {
    case 'n':
        if ( c2 == 'a' )
            month = 1 ;
        else
            month = 6 ;
        break ;
    case 'r':
        if ( c2 == 'a' )
            month = 3 ;
        else
            month = 4 ;
        break ;
    case 'b':
        month = 2 ;
        break ;
    case 'y':
        month = 5 ;
        break ;
    case 'l':
        month = 7 ;
        break ;
    case 'g':
        month = 8 ;
        break ;
    case 'p':
        month = 9 ;
        break ;
    case 't':
        month = 10 ;
        break ;
    case 'v':
        month = 11 ;
        break ;
    case 'c':
        month = 12 ;
        break ;
    }//end switch
}//end 3-param Month ctor

I get this error

.\prob1.func.cpp(4) : error C2436: 'setMbyNcheck' : member function or nested class in constructor initializer list

here's my setMbyNcheck:

void Month::setMbyNcheck ( char c1 , char c2 , char c3 )
{
    c1 = tolower ( c1 ) ;
    c2 = tolower ( c2 ) ;
    c3 = tolower ( c3 ) ;

    if ( c3 != 'n' && c3 != 'b' && c3 != 'r' && c3 != 'y' && c3 != 'l' && c3 != 'g' && c3 != 'p' && c3 != 't' && c3 != 'v' && c3 != 'c' )
    {
        cout << "Error:  Invalid Month!  -  Exiting Program" << endl ;
        exit ( 1 ) ;
    }
    else if ( c3 != 'a' && c3 != 'e' && c3 != 'p' && c3 != 'u' && c3 != 'c' && c3 != 'o' )
    {
        cout << "Error:  Invalid Month!  -  Exiting Program" << endl ;
        exit ( 1 ) ;
    }
    else if ( c1 != 'j' && c1 != 'f' && c1 != 'm' && c1 != 'a' && c1 != 's' && c1 != 'o' && c1 != 'n' && c1 != 'd' )
    {
        cout << "Error:  Invalid Month!  -  Exiting Program" << endl ;
        exit ( 1 ) ;
    }
}

Is this enough information?

i don't understand :(

>i don't understand
What's there not to understand? You can't do this:

class foo {
public:
  foo(): bar() {}
  void bar() {}
};

You fix it by taking bar out of the initializer list and putting it in the constructor body:

class foo {
public:
  foo()
  {
    bar();
  }
  void bar() {}
};

The error is hardly cryptic.

The error is hardly cryptic.

for someone who understand ctors very well, perhaps.

So how would I modify it from here:

class Month
{
public:
    Month ( char , char , char )
    {
        setMbyNcheck ( char , char , char ) ;
    }
    Month ( int )
    {
        setMcheck ( int ) ;
    }
    Month ( ) ;

    void setMbyN ( istream & is ) ;
    void setM ( int ) ;
    void outMbyN ( ) ;
    void outM ( ) ;
    Month nextMon ( ) ;
private:
    void setMbyNcheck ( char , char , char ) ;
    void setMcheck ( int ) ;
    int month ;
} ;

>for someone who understand ctors very well, perhaps.
Perhaps, but somehow I doubt it. You're told exactly what name is causing the problem and exactly where it is (the initializer list). Since there's only one thing in the initializer list, it's pretty obvious that it shouldn't be there whether you understand constructors or not.

>So how would I modify it from here:

class Month
{
public:
    Month ( char a, char b, char c )
    {
        setMbyNcheck ( a , b , c ) ;
    }
    Month ( int i )
    {
        setMcheck ( i ) ;
    }
    Month ( ) ;

    void setMbyN ( istream & is ) ;
    void setM ( int ) ;
    void outMbyN ( ) ;
    void outM ( ) ;
    Month nextMon ( ) ;
private:
    void setMbyNcheck ( char , char , char ) ;
    void setMcheck ( int ) ;
    int month ;
} ;

alright, thanks!
In my book it gives the following example:

BankAccount::BankAccount(double balance, double rate) : accountDollars(dollarsPart(balance)), accountCents(centsPart(balance))
{
...
}

This is what I was going by; how would I set it up like that?

ut oh, now i get this error: 1>.\prob1.func.cpp(4) : error C2084: function 'Month::Month(char,char,char)' already has a body. this is in my function.cpp file not my .h

>In my book it gives the following example
Okay, I'm thinking you're a tad confused about how initializers work. This is correct use of an initializer:

class foo {
  int bar;
public:
  foo(): bar ( 12345 ) {}
};

bar isn't a member function at all, it's an object. It looks like you're calling a function called bar, but the initializer is essentially calling bar's constructor with the value 12345. It works the same way as the following except the constructor is implicit because int is a built-in type:

class bar {
public:
  bar ( int init ) {}
};

class foo {
  bar b;
public:
  foo(): b ( 12345 ) {}
};

In the sample you provided, accountDollars and accountCents are both objects, most likely integers, like this:

class BankAccount {
  int accountDollars;
  int accountCents;
  double accountRate;
public:
  BankAccount ( double balance, double rate );
};

BankAccount::BankAccount ( double balance, double rate )
  : accountDollars ( 0 ), accountCents ( 0 )
{
  accountRate = rate;
}

Now, you can use the result of a function to initialize an object, and your sample does just that. Probably something like this:

#include <cmath>

class BankAccount {
  int accountDollars;
  int accountCents;
  double accountRate;
public:
  BankAccount ( double balance, double rate );
private:
  static int dollarsPart ( double amount );
  static int centsPart ( double amount );
};

BankAccount::BankAccount ( double balance, double rate )
  : accountDollars ( dollarsPart ( balance ) ),
  accountCents ( centsPart ( balance ) )
{
  accountRate = rate;
}

int BankAccount::dollarsPart ( double amount )
{
  return static_cast<int> ( amount );
}

int BankAccount::centsPart ( double amount )
{
  double save;
  return static_cast<int> ( std::modf ( amount, &save ) * 100 );
}

The point of an initializer list is to initialize the data members of your class. In your case, you want to do validation before setting the values of the data members, so the initializer list isn't appropriate as it stands. You'd want to do something more like this:

Month::Month ( char c1 , char c2 , char c3 )
{
    setMbyNcheck ( c1 , c2 , c3 )

    c2 = tolower ( c2 ) ;
    c3 = tolower ( c3 ) ;

    switch ( c3 ) 
    {
    case 'n':
        if ( c2 == 'a' )
            month = 1 ;
        else
            month = 6 ;
        break ;
    case 'r':
        if ( c2 == 'a' )
            month = 3 ;
        else
            month = 4 ;
        break ;
    case 'b':
        month = 2 ;
        break ;
    case 'y':
        month = 5 ;
        break ;
    case 'l':
        month = 7 ;
        break ;
    case 'g':
        month = 8 ;
        break ;
    case 'p':
        month = 9 ;
        break ;
    case 't':
        month = 10 ;
        break ;
    case 'v':
        month = 11 ;
        break ;
    case 'c':
        month = 12 ;
        break ;
    }//end switch
}//end 3-param Month ctor

>error C2084: function 'Month::Month(char,char,char)' already has a body.
You can only define the body once. You can do this:

class foo {
public:
  foo()
  {
    // Body here
  }
};

Or this:

class foo {
public:
  foo();
};

foo::foo()
{
  // Body here
}

But not both. In your case, you want the latter, so remove the bodies from your class definition and add the relevant member function calls to the existing bodies in your implementation file.

Oh ok, I think I get it now. So I was trying to call a function but I coded the function as the initializer, which is why I got the error; sound right?

Could I have done this:

Month::Month ( char c1 , char c2 , char c3 ) : setMbyN ( c1 , c2 , c3 )
{
setMbyNcheck ( c1 , c2 , c3 ) ;
}

Or would that not work?

edit] ooooopsy, was checking for a char not an int. took code out to prevent my self-esteem being lowered even further.

>Or would that not work?
It wouldn't work for the same reason, assuming setMbyN is a member function and not an object.

how about this:

Month::Month ( char c1 , char c2 , char c3 ) : m1 ( setMbyN ( c1 , c2 , c3 ) )
{
setMbyNcheck ( c1 , c2 , c3 ) ;

}

hey guess what.
another error - logic this time!

void Month::setMbyN ( istream & is )
{
    const string name[] = { "jan" , "feb" , "mar" , "apr" , "may" , "jun" , "jul" , "aug" , "sep" , "oct" , "nov" , "dec" } ;
    
    char c1 ;
    char c2 ;
    char c3 ;

    is >> c1 >> c2 >> c3 ;

    c1 = tolower ( c1 ) ;
    c2 = tolower ( c2 ) ;
    c3 = tolower ( c3 ) ;

    string mName ;
    mName = c1 + c2 + c3 ;

    int k = 0 ;
    while ( k < 11 )
    {
        if ( mName == name[k] )
        {
            month = k + 1 ;
            break ;
        }//end if
        ++k ;
    }//end while
    if ( k > 11 )
    {
        cout << "Error:  Invalid Month!  -  Exiting Program" ;
        exit ( 1 ) ;
    }//end if
}//end setMbyN

If it's simple just tell me to figure it out on my own.

>mName = c1 + c2 + c3 ;
This doesn't do what you think it does. c1, c2, and c3 are all integral values, so the + operator performs integral addition, not string concatenation. Why not just read the input into a string from the start?

void setMbyN ( istream & is )
{
    const string months[] = {
      "jan", "feb", "mar", "apr", "may", "jun",
      "jul", "aug", "sep", "oct", "nov", "dec"
    };

    string mName;

    is>> setw ( 3 ) >> mName;

    for ( string::size_type i = 0; i < mName.size(); i++ )
      mName[i] = tolower ( mName[i] );

    month = 0;

    for ( size_t i = 0; i < 12; i++ ) {
      if ( months[i] == mName ) {
        month = i + 1;
        break;
      }
    }

    if ( month == 0 )
      throw runtime_error ( "Invalid month" );
}
commented: great function! worked excellent! +4

thanks!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.