This works because a class
You invoke a class member function, or method, by using a class object. You do so by using the dot membership operator:
cout << Bozetta.Retort();
This invokes the Retort() member function, and whenever the code for that function refers to a particular data member, the function uses the value that member has in the bozetta object.
Class Constructors and Destructors
At this point, you need to do more with the Stock class. There are certain standard functions, called
One of C++’s aims is to make using class objects similar to using standard types. However, the code provided so far in this chapter doesn’t let you initialize a Stock object the way you can an ordinary int or struct. That is, the usual initialization syntax doesn’t carry over for the Stock type
int year = 2001; // valid initialization
struct thing
{
char * pn;
int m;
};
thing amabob = {"wodget", -23}; // valid initialization
Stock hot = {"Sukie's Autos, Inc.", 200, 50.25}; // NO! compile error
The reason you can’t initialize a Stock object this way is because the data parts have private access status, which means a program cannot access the data members directly. As you’ve seen, the only way a program can access the data members is through a member function. Therefore, you need to devise an appropriate member function if you’re to succeed in initializing an object. (You could initialize a class object as just shown if you made the data members public instead of private, but making the data public goes against one of the main justifications for using classes: data hiding.)
In general, it’s best that all objects be initialized when they are created. For example, consider the following code:
Stock gift;
gift.buy(10, 24.75);
With the current implementation of the Stock class, the gift object has no value for the company member. The class design assumes that the user calls acquire() before calling any other member functions, but there is no way to enforce that assumption. One way around this difficulty is to have objects initialized automatically when they are created. To accomplish this, C++ provides for special member functions, called
Declaring and Defining Constructors
Now you need to build a Stock constructor. Because a Stock object has three values to be provided from the outside world, you should give the constructor three arguments. (The fourth value, the total_val member, is calculated from shares and share_val, so you don’t have to provide it to the constructor.) Possibly, you may want to provide just the company member value and set the other values to zero; you can do this by using default arguments (see Chapter 8, “Adventures in Functions.”). Thus, the prototype would look like this:
// constructor prototype with some default arguments
Stock(const string & co, long n = 0, double pr = 0.0);
The first argument is a pointer to the string that is used to initialize the company string member. The n and pr arguments provide values for the shares and share_val members. Note that there is no return type. The prototype goes in the public section of the class declaration.
Next, here’s one possible definition for the constructor:
// constructor definition
Stock::Stock(const string & co, long n, double pr)
{
company = co;
if (n < 0)
{
std::cerr << "Number of shares can't be negative; "
<< company << " shares set to 0.\n";
shares = 0;
}
else
shares = n;
share_val = pr;
set_tot();
}
This is the same code that the acquire() function used earlier in this chapter. The difference is that in this case, a program automatically invokes the constructor when it declares an object.
Member Names and Parameter Names
Often those new to constructors try to use the class member names as parameter names in the constructor, as in this example:
// NO!
Stock::Stock(const string & company, long shares, double share_val)
{
...
}
This is wrong. The constructor arguments don’t represent the class members; they represent values that are assigned to the class members. Thus, they must have distinct names, or you end up with confusing code like this:
shares = shares;