Using private variable from class without getters and setters

Pages: 12
I am trying to practice more on using classes and I have created a banking program but I need to save and load the data but I need to access the private datam which i cannot do. I dont think getters and setters would be appropriate but idk, also I dont think making the save and load functions part of the banking class is the way to go either unless i make a IO class and make it a friend of Account, or make it a child of Account, but im unsure about that as well.

Aside from that, I have a checking class and a savings class and I'm a little confused on what goes in them if i define the stuff to be inherited from in Account. Checking and savings are different types of accounts so they should inherit from that but im not sure what to do there.

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <iostream>
#include <fstream>


class Account
{
    public:
        void CreateAccount(std::string, std::string, short unsigned int);
        void LoginToAccount();
        float Deposit();
        float Withdraw();
        float ShowBalance(float) const;

    private:
        std::string firstName = "Default_First";
        std::string lastName = "Default_Last";
        short unsigned int userPinNumber = 0;
        float accountBalance = 0;
        float depositAmount = 0;
        float withdrawAmount = 0;
};

class Checking : public Account
{
    public:

    private:
};

class Savings : public Account
{
    public:

    private:
};

void Account::CreateAccount(std::string firstName, std::string lastName, short unsigned int)
{
    std::cout << "Please enter your first name" << std::endl;
    getline(std::cin, firstName);

    std::cout << "\nNow please enter your last name" << std::endl;
    getline(std::cin, lastName);

    std::cout << "\nThank you " << firstName << " " << lastName << " Now please enter your pin" << std::endl;
    std::cin >> userPinNumber;
}

void Account::LoginToAccount()
{

}

float Account::Deposit()
{
    std::cout << "How much would you like to deposit?" << std::endl;
    std::cin >> depositAmount;

    return depositAmount;
}

float Account::Withdraw()
{

}

void Save()
{
    std::ofstream saveFile;
    saveFile.open("Save.txt");

    Account account;

    saveFile << account.firstName << std::endl;
    saveFile << account.lastName << std::endl;
    saveFile << account.userPinNumber << std::endl;
    saveFile << account.accountBalance << std::endl;
}

void Load()
{

}

int main()
{
    std::cout << "Welcome to the bank, what would you like to do?\n" << std::endl;

    std::cout << "1) Create Account" << std::endl;
    std::cout << "2) Login to existing account" << std::endl;

    int choice = 0;

    std::cin >> choice;


    std::string firstName, lastName;
    short unsigned int pin = 0;
    Account account;

    switch(choice)
    {
        case 1:
            account.CreateAccount(firstName, lastName, pin);
            break;
        case 2:
            account.LoginToAccount();
            break;
        default:
            std::cout << "Error" << std::endl;
    }

    return 0;
}
I dont think making the save and load functions part of the banking class is the way to go either

That is the way to go.

Make Save and Load class functions. The object should take care of itself. It should populate itself from file and it should save its own internals to file.

I want to be able to tell the class the name of a file in the constructor, and then I'm done; the object is populated and ready to use. I want to be able to tell the class to save itself, and then I'm done. The class should take care of itself, so I don't have to, and so that I know it works. If I have external functions for that, they can get out of sync. If I have to provide IO functions and so on, that's more that can go wrong.

A good object takes care of itself so I don't have to waste my cognition worrying about it.
Last edited on
Ok, but what happens if I have tons of different classes? should all of those save to separate files? I assume that if one class needed to load data from another class it could just load that save file and find the data, so thats no problem, but usually when i see save files for games or programs, its usually only just one file, especially games, do they just append all their data to one file and overwrite certain spots in the file? i'm trying to think in the long term here and not just within the scope of these classes and this program.
Last edited on
should all of those save to separate files?

Your save and load methods shouldn't save and load to files, they should save and load to streams.

I really like Repeater's phrase "the object should take care of itself." Let me add one that's been useful to me: the object's methods shouldn't necessarily do the work, they should make doing the work easy. If the save/load methods take the name of a file as a parameter then all you can do is write the data to a file, and where in the file? At the beginning? Will you overwrite file if it's already there? Or at the end? Blech! That's so restrictive.

On the other hand, if it takes a stream then it's very flexible. Let the caller worry about whether it's writing the data to the beginning or end of the stream or to a string stream or to cout or an encryption stream or whatever.
The class should take care of itself. If you have many different objects that need to save to disk, only that class knows what the data means. Only that class understands how much data that is, and what of the data is an int and what's a double and so on.

So if you need all objects to write to a single file, each class at the point of saving needs to know about the single output stream, however you do that, and each class needs to be able to understand the data it reads.

i'm trying to think in the long term here

Long term, make each class responsible for itself. When you can trust a class to take care of itself, life is so much easier.

There's also a principle called YAGNI; You Ain't Gonna Need It. Make something that works well and abstracts well for the problem you have at hand. Right now, you have one object needing one data storage. Make that work well. If you write it simply, and make the class responsible for itself, you will be able to adapt it easily when you need to. If you try to guess now which, of all the possible infinite futures you're going to take and write code now for that one-in-a-million future, you will guess wrong and you will be screwing over your future self.

overwrite certain spots in the file?

That's a really bad idea. It ties your hands to never moving the data in the file, never changing the size of the data in the file. Sometimes, the saved file is just a zip file or some such that gets opened in memory.

should all of those save to separate files?

If that's the best way to deal with the problem at hand. Is it?
Last edited on
So how do programmers make games save all the data to one file if they just make each class responsible for saving its own data? This program doesn't require saving to separate files, at least I dont think so. I remember seeing the save file for game called Prison architect, and how they saved and loaded data was in the file there would be different blocks of text separated by tags, and I assume the data was saved and loaded by looping through the file until it found the start of the tag and then overwrote the data until it hit the end tag. So it certainly is possible to do something like that, it may take a little effort to write code to do all that but anything thats worth it at all takes time.
So how do programmers make games save all the data to one file if they just make each class responsible for saving its own data?


Each class could be responsible for saving its own data, but they could all save it to the same file. They could save it directly, or they could all make use of a common "data saver" class and let that class be in charge of how the data is actually written and read. There could be an object that quizzes every individual class that might want to save data for their data, or prompt each class to save their data. There could be an in-memory file that each object reads/writes, and only at the point of needing to write to disk does that in memory file get written.

There are a million ways to do it. Some of them are horribly complicated. Some are beautifully simple. Whatever you pick, it's always ging to be true that classes should be responsible for themselves, unless you're trying to write C code with C++ syntax.

I assume the data was saved and loaded by looping through the file until it found the start of the tag and then overwrote the data until it hit the end tag.

That will work so long as the size of the data to be written never, ever changes. So long as you know on day one what the final save file should look like in the far future. It's asking for trouble.
Last edited on
So basically I could just write a save/Load class and inherit from all other classes and save the data that way? I'm really trying hard to learn to use classes the correct way, I mean I understand that classes are just used to group code thats similar and to create interfaces for that code, but what I struggle with somewhat is putting code in the correct place, categorizing it so to speak, I made this post a while back: http://www.cplusplus.com/forum/beginner/222676/

No, you should have save/load methods of your class:
1
2
3
4
5
6
class MyClass {
public:
    void save(std::ostream &) const;
    void load(std::istream &);
    ...
};


Now it's easy to create << and >> operators:
1
2
std::ostream & operator <<(std::ostream &os, const MyClass &cl) { cl.save(os); return os; }
std::istream & operator >>(std::istream &is, MyClass &cl) { cl.load(is); return is; }


Now suppose your game has an instance of class Board and two instances of class Player. To save to a file you might write:

1
2
3
4
5
6
7
8
9
10
11
12
class Game {
public:
    Board board;
    Player player1, player2;

    bool save(const string &fileName)
    {
        ofstream of(fileName);
        of << board << player1 << player2;
        return of.good();
    }
};


See how easy that is? You build bigger pieces from smaller pieces.

I've never seen the operator keyword before, what does it do here, I looked it up and it says its used to overload operators, but i'm still a little confused on how its used, does it just allow you to use non overloadable operators in a stream?
So I was looking at the Doom 3 source code and it seems they do have a save and load class:

https://github.com/id-Software/DOOM-3-BFG/blob/master/neo/d3xp/gamesys/SaveGame.cpp
https://github.com/id-Software/DOOM-3-BFG/blob/master/neo/d3xp/gamesys/SaveGame.h

I looked through the other classes and it seems they have the function for saving and restoring in some of the classes, I could just do something like that right? I'm no judge of good code but i've heard that Doom 3's source code is supposed to be really well written, but idk maybe someone on here could tell me.
The most important thing to do, is to do it.

Look at the program you've written and the needs of it, and make saving and loading work. Trying to guess now what you're going to need in the future is a fool's errand. Keep it simple, keep it well encapsulated, keep individual classes the masters of their own data. And just do it.

Then, you'll have something that works. When you have something new that needs more capability, you'll know exactly what problem you're trying to solve. You can spend years architecture astronauting and reading up on how other, completely different systems with different needs did it; you can spend an hour just doing it for yourself. Just do it.
Last edited on
Thats the problem though, I really dont know what is needed, and when I post my program for help, 9 times out of 10 i'm told i'm not doing things right, i'm missing something. I learned online from watching tutorials on youtube, so I really only understand how C++ works on the surface and understand very little on whats going on behind the scenes in terms of when I write some code how much it is impacting my program or the problems some code will cause later on etc. I think if I understood that a little more I could make better design decisions, because I just see text, but idk. Is there a site anywhere that could tell me stuff like that? one that doesn't take 5 paragraphs to explain one thing, perhaps one that just explains stuff concisely.

I desperately want to just code the right way but it seems like everyone tells me im always doing something wrong, and I try to learn from that but idk, it just seems like i can never get my code up to standards.
I desperately want to just code the right way


Harsh truth time, perhaps. Firstly, there is no right way. There is no right way. There is no right way. The best you can hope for is a solution that meets all the needs and constraints at hand, which will include such things as performance needs and hardware limitations and the abilities of the writers and the abilities of the maintainers and the known need for future expansion and many, many other factors. There is no right way. There are only ways that meet the requirements and needs to greater or lesser extents.

As a beginner, you'll make mistakes. You cannot expect to write as if you have ten years' experience and have made many, many mistakes, because you don't have ten years' experience. You haven't gathered the practical wisdom to do so.

You can read about design patterns and you can read what's considered "good code" to see how experienced, skilled coders wrote code for their particular needs and their particular situation (although without any knowledge and experience of your own to hang it on, how could you possibly know if what you're reading is good or bad? It'd be cargo-cult programming, copying blindly), but if you're hoping to be able to read something now that will distill a decade's wisdom into your head, you're seeking the impossible.

I learned online from watching tutorials on youtube

That's a way to learn the syntax and a handful of very basic techniques. You won't learn how to actually program like that (although once you have enough knowledge and experience that you have a mental context that helps you understand, series like "Handmade Hero" are very educational; in my opinion, beginners get very little out of watching "Handmade Hero", but more experienced coders can really get a lot out of it. Watching someone mouth-breath their way through slowly typing in a way to reverse a string over ten minutes is not useful to anyone).

Have you written your save/load methods yet? If you've written nothing, then you're suffering from analysis paralysis. Code that doesn't work because it doesn't exist scores zero points and does nobody any good; particularly you.

I really dont know what is needed,

No worries. I can help you with that. You need to be able to enter an account's details and save them to disk. Then, restart the program and be able to load them from disk so that you can see that the details are the same.

That's what needed. Do it. Stop worrying. Do it. Do it. I'm really serious. Spending an hour making it work will do you so much more good and be so much more educational than trying to architecture astronaut some magical perfect solution inside your head.

When you've done it, you'll have so much more understanding. Show us your code. We'll suggest an alternative method. We'll suggest a way you can expend your program. Perhaps handling different kinds of objects. Perhaps writing binary rather than text. Perhaps a level of abstraction. Right now, we can't help you learn because you're stuck in your own analysis cycle, intimidated by what you don't know. Just start coding; start learning.

perhaps one that just explains stuff concisely.

This is going to take a long time. You can read a single sentence telling you how to chisel granite, but that won't make you a sculptor.

it just seems like i can never get my code up to standards.

You can't code as if you have a decade's experience? Keep going and see how you're doing in a decade's time.

Definitely, definitely STOP WORRYING about the fact that you're not writing perfect code. Just enjoy writing it, enjoy learning, accept that beginners are not as good at anything as people who've been doing it for a lifetime.
Last edited on
I've just written some save and load classes, I tested out one function that saves the first name but it's not saving to file. The file is created but its not saving any text. I can write a save file and have it save to disc, but I never wrote one that uses classes before so im a little stuck.

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include <iostream>
#include <fstream>
#include <limits>
#include <Save.h>
#include <Restore.h>

using namespace std;

class SaveGame
{
    public:
        SaveGame();
        ~SaveGame();
        std::string WriteFirstName(std::string &_firstName);
        void WriteLastName(std::string &_lastName);
        void WriteUserPinNumber(int &_userPin);
        void WriteAccountBalance(float &_accountBalance);
        void Write();

    private:
        std::ofstream file;
        std::string _firstName;

};

SaveGame::SaveGame()
{

}

SaveGame::~SaveGame()
{

}

void SaveGame::Write()
{
    file.open("file.txt");

    file << WriteFirstName(_firstName) << std::endl;

    file.close();
}

std::string SaveGame::WriteFirstName(std::string &_firstName)
{
    return _firstName;
}


class RestoreGame
{
    public:
        void ReadFirstName(std::string &_firstName);
        void ReadLastName(std::string &_lastName);
        void ReadUserPinNumber(int &_userPin);
        void ReadAccountBalance(float &_accountBalance);
        void Read();

    private:
        std::ifstream file;

};


class Account
{
    public:
        Account() = default;
        ~Account() = default;
        void CreateAccount();
        void LoginToAccount();
        float Deposit();
        float Withdraw();
        float ShowBalance(float) const;

    private:
        std::string firstName = "Default_First";
        std::string lastName = "Default_Last";
        short unsigned int userPinNumber = 0;
        float accountBalance = 0;
        float depositAmount = 0;
        float withdrawAmount = 0;
};

class Checking : public Account
{
    public:

    private:
};

class Savings : public Account
{
    public:

    private:
};

void Account::CreateAccount()
{
    SaveGame sg;

    std::cout << "Please enter your first name" << std::endl;
    getline(std::cin, firstName);

    sg.WriteFirstName(firstName);
    std::cout << sg.WriteFirstName(firstName) << std::endl;
    sg.Write();

    std::cout << "\nNow please enter your last name" << std::endl;
    getline(std::cin, lastName);

    std::cout << "\nThank you " << firstName << " " << lastName << " Now please enter your pin" << std::endl;
    std::cin >> userPinNumber;
}

void Account::LoginToAccount()
{
    int pinNumber = 0;

    std::cout << "Hello " << firstName << " Please enter your pin number" << std::endl;
    std::cin >> pinNumber;

    if(pinNumber == userPinNumber)
    {
        std::cout << "Thank you." << std::endl;
    }
    if(pinNumber != userPinNumber)
    {
        std::cout << "The pin number you entered is incorrect." << std::endl;
    }
}

float Account::Deposit()
{
    std::cout << "How much would you like to deposit?" << std::endl;
    std::cin >> depositAmount;

    return depositAmount;
}

float Account::Withdraw()
{
    std::cout << "How much would you like to withdraw?" << std::endl;
    std::cin >> withdrawAmount;

    if(withdrawAmount <= accountBalance)
    {
        withdrawAmount -= accountBalance;
        std::cout << "You withdrew " << withdrawAmount << " your new balance is " << accountBalance << std::endl;
    }
    else if(withdrawAmount > accountBalance)
    {
        std::cout << "You cannot withdraw more money than you have in your account" << std::endl;
    }

    return accountBalance;
}


int main()
{
    std::cout << "Welcome to the bank, what would you like to do?\n" << std::endl;

    std::cout << "1) Create Account" << std::endl;
    std::cout << "2) Login to existing account" << std::endl;

    int choice = 0;

    std::cin >> choice;

    Account account;

    switch(choice)
    {
        case 1:
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            account.CreateAccount();
            break;
        case 2:
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            account.LoginToAccount();
            break;
        default:
            std::cout << "Error" << std::endl;
    }

    return 0;
}
I see you have a SaveGame class, that contains an object of type string named _firstName. Your SaveGame class is really just a SaveAccount class, it looks like.

Why make it a class? Classes are for situations in which you will need many of a particular object. How many SaveGame classes are you going to need? If you had ten accounts to save, would you create ten SaveGame objects, or would you just use the same SaveGame object for all of them?

Why does SaveGame need to have _firstName as an internal variable? Surely the account is for holding that information? The SaveGame object needs to know how to write variables to disk. That's all it needs to know.

I think you've aimed too high to begin with. I think you're trying to solve problems you don't have, because you want to make something elaborate; elaborate code that doesn't work is worse than simple code that does work. In fact, simple code that does work is better than elaborate code that does work.

Don't start with a separate class for writing to disk. Have your account class be able to write itself out to disk. That's all. Just make that work. Make something simple that works. If it's good enough, you're done. If it needs more, then do more.

1
2
3
4
5
6
7
8
9
10
11
Account::save(std::string& fileName)
{
    std::ofstream outFile(fileName);

    outFile <<   firstName
      <<  lastName
     <<    userPinNumber 
       <<  accountBalance;

    outFile.close();
}


This is the starting point. Make this work. You learn nothing by making something big that doesn't work. You learn a lot by making something simple that does work, and then slowly improving it.
Last edited on
Ok, I did this and it works, I can save and load from disc!

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <iostream>
#include <fstream>
#include <limits>
#include <Save.h>
#include <Restore.h>

class Account
{
    public:
        Account() = default;
        ~Account() = default;
        void CreateAccount();
        void LoginToAccount();
        float Deposit();
        float Withdraw();
        float ShowBalance(float) const;
        void SaveGame();
        void RestoreGame();

    private:
        std::string firstName = "Default_First";
        std::string lastName = "Default_Last";
        short unsigned int userPinNumber = 0;
        float accountBalance = 0;
        float depositAmount = 0;
        float withdrawAmount = 0;
};

void Account::CreateAccount()
{

    std::cout << "Please enter your first name" << std::endl;
    getline(std::cin, firstName);

    std::cout << "\nNow please enter your last name" << std::endl;
    getline(std::cin, lastName);

    std::cout << "\nThank you " << firstName << " " << lastName << " Now please enter your pin" << std::endl;
    std::cin >> userPinNumber;

    SaveGame();
}

void Account::LoginToAccount()
{
    int pinNumber = 0;

    RestoreGame();

    std::cout << "Hello " << firstName << " Please enter your pin number" << std::endl;
    std::cin >> pinNumber;

    if(pinNumber == userPinNumber)
    {
        std::cout << "Thank you." << std::endl;
    }
    if(pinNumber != userPinNumber)
    {
        std::cout << "The pin number you entered is incorrect." << std::endl;
    }
}

float Account::Deposit()
{
    std::cout << "How much would you like to deposit?" << std::endl;
    std::cin >> depositAmount;

    return depositAmount;
}

float Account::Withdraw()
{
    std::cout << "How much would you like to withdraw?" << std::endl;
    std::cin >> withdrawAmount;

    if(withdrawAmount <= accountBalance)
    {
        withdrawAmount -= accountBalance;
        std::cout << "You withdrew " << withdrawAmount << " your new balance is " << accountBalance << std::endl;
    }
    else if(withdrawAmount > accountBalance)
    {
        std::cout << "You cannot withdraw more money than you have in your account" << std::endl;
    }

    return accountBalance;
}


void Account::SaveGame()
{
    std::ofstream saveFile("File.txt");

    saveFile << firstName << std::endl;
    saveFile << lastName << std::endl;
    saveFile << userPinNumber << std::endl;
    saveFile << accountBalance << std::endl;

    saveFile.close();
}

void Account::RestoreGame()
{
    std::ifstream restoreFile("File.txt");

    restoreFile >> firstName;
    restoreFile >> lastName;
    restoreFile >> userPinNumber;
    restoreFile >> accountBalance;

    restoreFile.close();
}

int main()
{
    std::cout << "Welcome to the bank, what would you like to do?\n" << std::endl;

    std::cout << "1) Create Account" << std::endl;
    std::cout << "2) Login to existing account" << std::endl;

    int choice = 0;

    std::cin >> choice;

    Account account;

    switch(choice)
    {
        case 1:
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            account.CreateAccount();
            break;
        case 2:
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            account.LoginToAccount();
            break;
        default:
            std::cout << "Error" << std::endl;
    }

    return 0;
}
Good job.

Expansion task; be able to save more than one account in the same file, and when the user requests to login to an account, give them a list of saved accounts to pick from.

Then, when that's working, have it load the chosen account.
ok, i'm a little stuck on how to actually get the accounts from the file. I know how to write them both to file I believe, I have had very little experience writing multiple different things to files and getting them. The rest of the stuff seems simple. I'm sure I would have to create an array or vector to hold multiple pieces of account info, then the name of the accounts can be shown and then the user can select them.
Last edited on
Alternatively, read from the file and check the account number. If it's the account number the user asked for, bingo. That's the account you want. If it's not, read again, without having closed and reopened the file.

Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bool have_found_right_account = false;
// open file up here somewhere
while (have_found_right_account)
{
    restoreFile >> accountNumber;
    restoreFile >> firstName;
    restoreFile >> lastName;
    restoreFile >> userPinNumber;
    restoreFile >> accountBalance;

   if ( accountNumber == accountNumberToOpen)
  {
    have_found_right_account = true;
  }
}


Either way, you can see now that maybe you need to separate out the actions of opening/closing the file, and reading an account from it. Opening the file, reading one account, and closing the file no longer works. It did work; it was a good solution. But now you have new requirements, so you need to alter the code. What you didn't do was try to guess in advance what you would need and spend ages writing some huge, elaborate system based on a guess.

This is programming. Thinking about the problem, thinking about solutions in such a way that you can write the solution as a program. All the rest is memorising syntax. THIS is programming.



Pages: 12