0

I'm reading the book "a complete guide to c++". I think there is a typo there on page 252. So I have three files as the following.

In file account.h,

// account.h
// Defining the class Account.     class definition (methods prototypes) is usually put in the header file
// ---------------------------------------------------
#ifndef _ACCOUNT_                                             // if _ACCOUNT_ is not defined
#define _ACCOUNT_

#include <iostream>
#include <string>
using namespace std;


class Account
{
   private:
     string name;
     unsigned long nr;
     double balance;

   public:            //Public interface:
     bool init( const string&, unsigned long, double);
     void display();
};
#endif
// _ACCOUNT_

In file account.cpp,

// account.cpp
// Defines methods init() and display().
// ---------------------------------------------------
#include "account.h"             // Class definition
#include <iostream>
#include <iomanip>
using namespace std;
// The method init() copies the given arguments
// into the private members of the class.


bool Account::init(const string& i_name,
                   unsigned long i_nr,
                   double i_balance)
{
    if( i_name.size() < 1)
          return false;          // check data format to make sure it is valid
    name = i_name;
    nr = i_nr;
    balance = i_balance;
    return true;
}

// the method display() outputs private data.
void Account::display()
{
   cout << fixed << setprecision(2)
        << "--------------------------------------\n"
        << "Account holder:" << name << '\n'
        << "Account number:" << nr << '\n'
        << "Account balance:" << balance << '\n'
        << "--------------------------------------\n"
        << endl;
}

And finally, in file account_t.cpp

// account_t.cpp
// Uses objects of class Account.
// ---------------------------------------------------
#include "account.h"                 // header file which contains class definition; (prototype for member functions)

int main()
{
   Account current1, current2;       // create two instances with name current1, current2

   current1.init("Cheers, Mary", 1234567, -1200.99); 
// have to call the init function to initialize a Account object; init function is public; members properties are private;
// that's why can not do current1.name = "nana" outside of the class definition  
   current1.display();
   // current1.balance += 100;         // Error: private member
   current2 = current1;
   current2.display();

   current2.init("Jones, Tom", 3512347, 199.40);
   current2.display();
   Account& mtr = current1;     // create a reference, which points to object current1
   mtr.display();
   return 0;
}

I do not think it's correct; because obviously there is no way to get access the init member methods and the display member methods, right? I hope this is not a naive question.

EDIT: I tried to run the main function in file account_t.cpp, and got the following output.

~$ g++ account_t.cpp
/tmp/ccSWLo5v.o: In function `main':
account_t.cpp:(.text+0x8c): undefined reference to `Account::init(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long, double)'
account_t.cpp:(.text+0xb6): undefined reference to `Account::display()'
account_t.cpp:(.text+0xd5): undefined reference to `Account::display()'
account_t.cpp:(.text+0x132): undefined reference to `Account::init(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long, double)'
account_t.cpp:(.text+0x15c): undefined reference to `Account::display()'
account_t.cpp:(.text+0x176): undefined reference to `Account::display()'
collect2: error: ld returned 1 exit status

Any more comments are greatly appreciated.

user207421
  • 305,947
  • 44
  • 307
  • 483
kkxx
  • 571
  • 1
  • 5
  • 16
  • 4
    why would that be? both init() and display() are public. – Hafnernuss Nov 18 '19 at 08:52
  • 1
    Unrelated to your problem, but please note that all symbols starting with an underscore followed by an upper-case letter (like e.g. `_ACCOUNT_`) are reserved. See [What are the rules about using an underscore in a C++ identifier?](https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) for details. – Some programmer dude Nov 18 '19 at 08:53
  • >// current1.balance += 100; if this line is _not_ commented out in the book, than yes, thats an error. balance is inaccessible because its a private member. – Hafnernuss Nov 18 '19 at 08:55
  • 4
    Let's see, using a reserved identifier (`_ACCOUNT_`), including `` in an header without needing it, using `using namespace std;` in a header, using a `double` for money, using an `init` method and no constructor... and that on page 252? I'm not sure I'd recommend that book. – AProgrammer Nov 18 '19 at 08:56
  • 1
    Also, page 252 in a C++ book and no initializer list used... uh oh :p – Hafnernuss Nov 18 '19 at 08:57
  • @Hafnernuss, even though they are public, there is no way to find/load them at all in the file account_t.cpp, right? – kkxx Nov 18 '19 at 09:06
  • @kkxx Why not? What's your question? – user207421 Nov 18 '19 at 09:09
  • @kkxx I have added the answer to your edit in my question. For the next time that you ask a question, please include error messages immediately. It would have made answering the question much more straight-forward. – walnut Nov 18 '19 at 09:20

1 Answers1

6

There is no issue with what you are asking about. init and display are declared public in the definition of class Account and the file with the class definition is #includeed properly in the .cpp using these methods.

In order to use a function only its declaration is needed (which is in the class definition in the header which is included). The definition/implementation in the .cpp file is not needed to use the function.

Each .cpp is compiled individually (called a translation unit) and afterwards each use of a function (for which only the declaration might have been available) is linked to the correct definitions from the other translation units if necessary via the function's scope, name and signature. This is called the linking process.

An introductory book to C++ should explain how compilation and linking process work.

For g++, there is an easy way to compile all .cpp files individually as translation units and then directly link them together:

g++ account_t.cpp account.cpp

You always need to add all .cpp to the compiler invocation like that. (There are alternative ways, but this is the easiest one.)

However, as mentioned in the comments there are other issues with this program:

  1. _ACCOUNT_ is a reserved identifier that one may not #define in a program. All identifiers starting with an underscore followed by a capital letter are reserved. Using them causes undefined behavior.

  2. using namespace std; is bad, at the very least when used in a header file, see Why is "using namespace std;" considered bad practice?.

  3. Classes have constructors. One should not write init methods in most cases. The constructor is responsible for constructing and initializing class instances. But no constructor is used in the code.

  4. Money should never be stored in double, because arithmetic with double is imprecise. You should store the value in an integer type in dimensions of the smallest relevant unit of money (e.g. cents).

  5. #include <iostream> is not needed in the header file. One should avoid adding #includes that are not needed.

  6. init returns a boolean indicating successful initialization. But main never checks that value. If a function can fail with an error value, then you must check that error value returned by the function to make sure that continuing the rest of the program is safe.

Some of these points may be excusable as simplification for a beginner program, depending on how far the book got at this point (though 200+ pages should already cover a lot), but others aren't.


Example of constructor use doing the same thing:

class Account
{
   private:
     string name;
     unsigned long nr;
     double balance;

   public:            //Public interface:
     Account(const string&, unsigned long, double);
     void display();
};
Account::Account(const string& i_name,
                   unsigned long i_nr,
                   double i_balance)
    : name(i_name), nr(i_nr), balance(i_balance)
{
}
int main()
{
   Account current1("Cheers, Mary", 1234567, -1200.99); 
// Create Account instance and initialize private members; constructor is public; members properties are private;
// that's why can not do current1.name = "nana" outside of the class definition  
   current1.display();
   // current1.balance += 100;         // Error: private member
   Account current2 = current1; // Create second Account instance and copy private members from first one
   current2.display();

   current2 = Account("Jones, Tom", 3512347, 199.40); // Replace instance with a copy of a new one
   current2.display();
   Account& mtr = current1;     // create a reference, which points to object current1
   mtr.display();
   return 0;
}

The i_name.size() < 1 check (which is weirdly written, why not i_name.size() == 0?) would be realized by throwing an exception from the constructor:

Account::Account(const string& i_name,
                   unsigned long i_nr,
                   double i_balance)
    : name(i_name), nr(i_nr), balance(i_balance)
{
    if(i_name.size() == 0) {
       throw invalid_argument("Account does not accept empty names!");
    }
}

This requires #include<stdexcept> and is a more advanced topic.

walnut
  • 21,629
  • 4
  • 23
  • 59
  • Hi, thank you for detailed explanations. One more unrelated question, if init method is not needed, then how to construct an instance / object of this class? – kkxx Nov 18 '19 at 09:26
  • @kkxx See my edit. Your book should talk about class constructors (and very soon after introducing classes imho) – walnut Nov 18 '19 at 09:36
  • 1
    @kkxx The book seems to introduce constructors in the next chapter. Note however that the book is from 2001/2002. The C++ that it is going to be teaching you has been out-dated for a while. In particular there were many changes to the language in 2011 with the C++11 standard. You will not learn modern C++ from any book from before that. – walnut Nov 18 '19 at 09:54
  • @kkxx Have a look at the [C++ book guide here on stackoverflow](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). Note that there have been a few more C++ standards since C++11: C++14 and C++17 and C++20 is currently in drafting. I suggest you use a book that at least covers C++11, the newer standards are nice-to-have, but not vital to a beginner. Everything only teaching C++98 and/or C++03 is out-dated due to the significant changes in C++11. (Although most of C++ is backwards-compatible with the older versions, the programming style changed a lot.) – walnut Nov 18 '19 at 10:29
  • Thank you indeed. I have found a link learnCpp.com which is really good. https://www.learncpp.com/?ref=hackr.io – kkxx Nov 18 '19 at 10:49