Reputation: 95
I am trying to make the overloaded constructor call the default constructor, but it only gives me garbage numbers. What I want it to do is recognize if the date entered is invalid, and so default it to 1/1/2000.
#include <iostream>
#include <iomanip>
#include <string>
#include "date.h"
using namespace std;
Date::Date ()
{
month = 1;
day = 1;
year = 2000;
monthName = "Jan ";
format = 'D';
valid = true;
}
Date::Date (int m, int d, int y)
{
valid = false;
if (y > 0)
{
//January
if (m == 1 && d >= 1 && d <= 31)
{
month = m; day = d; year = y;
monthName = "Jan "; valid = true;
}
//February
else if (m == 2 && d >= 1 && d <= 28)
{
month = m; day = d; year = y;
monthName = "Feb "; valid = true;
}
//etc.
}
if (valid == false)
Date ();
}
Upvotes: 0
Views: 192
Reputation: 63755
A constructor can only be called when an object is first being constructed. It is not a general-purpose function.
Your code here is doing that - constructing a new Date
, but not doing anything with it.
Date ();
You can achieve what you want by assigning that new, default-constructed Date
.
*this = Date();
Edit: Be sure that valid
is what you want it to be in this case.
Upvotes: 1
Reputation: 145239
First, in
Date ();
you are constructing a temporary, and discarding it. C++ does have low level facilities to call a constructor on existing storage, but an ordinary constructor call just creates a new object.
Also note that
if (valid == false)
can and should be more cleanly expressed as just
if( not valid )
or if you like symbolic operators,
if( !valid )
Now, the intent of the original code can be expressed
by forwarding to a common constructor (a natural way would be by invoking and passing the result of a month name function), or
by default-constructing first and then modifying, or
by assigning a default-constructed instance.
These are in order of most clean to most unclean.
Do note that assigning a default-constructed instance, the most dirty option above, and doing nothing more, as suggested in another answer, will set the valid
member to true
, thus removing all information about the fact that the constructor arguments were invalid…
However, none of these options are good! For the intent, that of treating an argument error as a request for a default, is itself very ungood. Instead, when you detect an argument error, throw an exception or terminate, so that the client code will not have on hand a possibly unexpected object.
E.g., do
if( not valid ) { throw std::runtime_error( "Date::<init>: invalid args" ); }
Some folks prefer to use std::logic_error
or std::range_error
.
In passing, with Visual C++ force-include <iso646.h>
to get support for the C++ keywords (less imprecisely, the reserved words) and
, or
and not
.
Example of (not recommended! but least dirty of original intent implementations) common constructor approach:
class Date
{
private:
int day_;
int month_;
int year_;
string month_name_;
bool is_valid_;
Date( int month, int day, int year, const string& month_name );
public:
static
auto month_name_for( int month, int day, int year )
-> string;
Date();
Date( int month, int day, int year );
};
Date::Date( const int m, const int d, const int y, const string& month_name )
: month_( month_name == ""? 1 : m )
, day_( month_name == ""? 1 : d )
, year_( month_name == ""? 2000 : y )
, month_name_( month_name == ""? "Jan" : month_name )
, is_valid_( month_name != "" )
{}
auto Date::month_name_for( const int m, const int d, const int y )
-> string
{
if( y > 0 )
{
if( m == 1 && 1 <= d && d <= 31 ) { return "Jan "; }
const int days_in_feb = 28; // TODO: correct for leap year
if( m == 2 && 1 <= d && d <= days_in_feb ) { return "Feb "; }
if( m == 3 && 1 <= d && d <= 31 ) { return "Mar "; }
//etc.
}
return "";
}
Date::Date ()
: Date( 0, 0, 0, "" )
{}
Date::Date( const int m, const int d, const int y )
: Date( m, d, y, month_name_for( m, d, y ) )
{}
Example of each constructor ensuring a valid object (recommended):
class Date
{
private:
int day_;
int month_;
int year_;
public:
static
auto month_name_for( int month )
-> string;
static
auto is_valid( int month, int day, int year )
-> bool;
Date();
Date( int month, int day, int year );
};
auto Date::month_name_for( const int m )
-> string
{
static const string names[] = { "Jan", "Feb" }; // Etc.
return (1 <= m && m <= 12? names[m-1] : "");
}
auto Date::is_valid( const int m, const int d, const int y )
-> bool
{
if( y > 0 )
{
if( m == 1 && 1 <= d && d <= 31 ) { return true; }
const int days_in_feb = 28; // TODO: correct for leap year
if( m == 2 && 1 <= d && d <= days_in_feb ) { return true; }
if( m == 3 && 1 <= d && d <= 31 ) { return true; }
//etc.
}
return false;
}
Date::Date ()
: Date( 1, 1, 2000 )
{}
Date::Date( const int m, const int d, const int y )
: month_( m ), day_( d ), year_( y )
{
if( not is_valid( m, d, y ) )
{
throw runtime_error( "Date::<init>: invalid arguments" );
}
}
Upvotes: 1