This works because a class is a user-defined type.

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 constructors and destructors, that you should normally provide for a class. Let’s talk about why they are needed and how to write them.

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 class constructors, especially for constructing new objects and assigning values to their data members. More precisely, C++ provides a name for these member functions and a syntax for using them, and you provide the method definition. The name is the same as the class name. For example, a possible constructor for the Stock class is a member function called Stock(). The constructor prototype and header have an interesting property: Although the constructor has no return value, it’s not declared type void. In fact, a constructor has no declared type.

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;

Перейти на страницу:

Все книги серии Developer's Library

Похожие книги