Creating immutable class.

My assignment asks me to create my own class, the class must be immutable. So I wrote up everything how I thought it should work, however, I've run into some issues. In my implementation file under "int Time::getTotalSeconds() const" the "totalSeconds" says "Error: expression must be a modifiable value". This happens several other places, basically anytime I use one of my variables from the class.

I tried removing all my uses of const, which let me run the program, however this would ruin the whole immutable thing.

What steps to do need to take to maintain immutability and still accomplish my goals?

1
2
3
4
5
6
7
8
9
10
  //************
//RelationType 
//************

#include <iostream>

using namespace std;

enum RelationType {BEFORE, SAME, AFTER};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//***************************************
// SPECIFICATION FILE (Time.h)
//This file gives the specificaiton of a
//Time abstract data type.
//***************************************

#include "RelationType.h"

class Time
{
public:
	//Constructors
	Time();
	//Post: minutes and seconds have been set to 0
	Time (int inputMinutes, int inputSeconds);
	//Post: Time is set accorind to incoming parameters
	int getMinutes() const;
	//Returns minutes
	int getSeconds() const;
	//Returns seconds
	int getTotalSeconds() const;
	//Returns time converted to seconds
	void AddTime(Time otherTime) const;
	//Adds two times together
	void SubtractTime(Time otherTime) const;
	//Subtracts two times
	RelationType ComparedTo(Time otherTime);
	//Compares one time to another time

private:
	int minutes;
	int seconds;
	int totalSeconds;
	int subtractSeconds;

}; 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//
//IMPLEMENTATION FILE
//

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

using namespace std;

Time::Time()
{
	minutes = 0;
	seconds = 0;
}

//***************************************************************************

Time::Time(int inputMinutes, int inputSeconds)
{
	minutes = inputMinutes;
	seconds = inputSeconds;
}

//***************************************************************************

int Time::getMinutes() const
{
	return minutes;
}

//***************************************************************************
int Time::getSeconds() const
{
	return seconds;
}

//***************************************************************************

int Time:: getTotalSeconds() const
{
	totalSeconds = minutes*60 + seconds;
	return totalSeconds;
}

//***************************************************************************

void Time::AddTime(Time otherTime) const
{
	minutes = minutes + otherTime.minutes;
	seconds = seconds + otherTime.seconds;

	cout << minutes << ':' << seconds << endl;
}

//***************************************************************************

void Time::SubtractTime(Time otherTime) const
{
	subtractSeconds = getTotalSeconds() - otherTime.getTotalSeconds();

	if (subtractSeconds < 0)
		cout << "0:00" << endl;
	else 
	{
		cout << subtractSeconds/60 << ":" << subtractSeconds%60 << endl;
	}
}

//****************************************************************************

RelationType Time::ComparedTo(Time otherTime)
{
	if (minutes < otherTime.minutes)
		return BEFORE;
	else if (minutes > otherTime.minutes)
		return AFTER;
	else if (seconds < otherTime.minutes)
		return BEFORE;
	else if (seconds > otherTime.minutes)
		return AFTER;
	else
		return SAME;
	}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//*****************************************************************************************
//This program creats two time functions and tests them with the get and compare functions.
//*****************************************************************************************

#include <iostream>
#include "Time.h"		//For Time class

using namespace std;

int main ()
{
	Time time1(4, 47);
	Time time2;
	int result;

	cout << "time1: " << time1.getMinutes() << ':'
		 << time1.getSeconds() << endl;
	cout << "time2: " << time2.getMinutes() << ':'
		 << time2.getSeconds() << endl;

	switch (time1.ComparedTo(time2))
	{
	case BEFORE: cout << "First time comes before second time."
					  << endl;
	case SAME: cout << "Times are both the same."
				    << endl;
	case AFTER: cout << "First time comes after second time."
					 << endl;
		break;
	}

	time1.AddTime(time2);
	time1.SubtractTime(time2);
	

	system("pause");
	return 0;
}
Last edited on
The whole point of declaring member functions as const is so you aren't able to modify any of the member variables in the class which you are trying to do in places like this.

1
2
3
4
5
int Time:: getTotalSeconds() const
{
	totalSeconds = minutes*60 + seconds;
	return totalSeconds;
}


In this specific case I would just make "totalSeconds" a local variable with a different name and return that as there is no reason to access the member variable there.

As for functions like "AddTime" you will have a hard time trying to keep that a const function and keeping the entire class immutable as you really need to access a member variable there. Do you have to use this example for a class? There are probably better examples you could use.
An immutable class should declare everything const, and all the class variables should be set in the constructor.

The AddTime() and SubtractTime() methods violate the immutability of the class by trying to modify it. They should return a new Time.

1
2
3
4
Time Time::AddTime( const Time& t )
{
	return Time( minutes + t.minutes, seconds + t.seconds );
}

Convenient to this is you can now overload the + operator using the method:

1
2
3
4
inline Time operator + ( const Time& a, const Time& b )
{
	return a.AddTime( b );
}


BTW, your constructors should also take care to normalize seconds and minutes such that seconds is never more than 59.

1
2
3
4
5
Time::Time(int inputMinutes, int inputSeconds)
{
	minutes = inputMinutes + inputSeconds / 60;
	seconds = inputSeconds % 60;
}

Hope this helps.
Using your idea of having a local function I've tried this:

1
2
3
4
5
6
7
int Time:: getTotalSeconds() const
{
	int totalSeconds;
	totalSeconds = minutes*60 + seconds;
	cout << totalSeconds << endl;
	return totalSeconds;
}


and this:

1
2
3
4
5
6
7
8
9
void Time::AddTime(Time otherTime) const
{
	int addedMinutes;
	int addedSeconds;
	addedMinutes = minutes + otherTime.minutes;
	addedSeconds = seconds + otherTime.seconds;

	cout << addedMinutes << ':' << addedSeconds << endl;
}


The program runs now, but I don't have the knowledge or experience to see if this is really doing what I'm wanting. Is this going to work or am I missing something?
The methods of the class really shouldn't be writing to cout. That should be handled by overloading the operator<< to take a Time:

1
2
3
4
std::ostream& operator << ( std::ostream& outs, const Time& t )
{
  return outs << t.minutes << ":" << t.seconds;
}

Now you can use it properly:

1
2
3
cout << a.addTime( b ) << endl;
//or
cout << (a + b) << endl;  // assuming you overloaded operator+() as well 

Hope this helps.
Thanks for the help, I think I've got it up and working now. Thanks to you two everything has come together. I really appreciate all the help!
Last edited on
Topic archived. No new replies allowed.