The class Scope

I haven't misled you on that point; there is another scope called class scope, which applies to all member variables of a class. Variables with class scope occupy separate memory locations for each object; i.e., each object has its own separate set of member variables distinct from the member variables of any other objects.[12] In the case of StockItem, this set of member variables consists of m_InStock, m_Price, m_Name, m_Distributor, and m_UPC. Member functions of a class can access member variables of objects of that class without defining them, as though they were global variables.

[12] Actually, I'm describing "normal" member variables here. There is another kind of member variable, called a static member variable, which is shared among all objects of a given class. We won't be using this type of member variable in this book.

In addition to scope, each member variable has another attribute we have already encountered: an access specifier. The access of nonmember functions to any member variable or member function depends on the access specifier in effect when the member variable or function was declared. If you look back at Figure 6.3, you'll see that the line private: precedes the declaration of the member variables in the StockItem class. The keyword private is an access specifier, like public; however, where a public access specifier allows any function to access the items that follow it, a private access specifier allows only member functions to access items that follow it.

Susan had some more questions about access specifiers, including this new one, private:

Susan: It seems to me that the access specifiers act more like scope than anything. Are they about the same?

Steve: Yes, the difference between public and private is somewhat analogous to the difference between global and local variables, but the latter distinction affects where a variable is stored and when it is initialized, whereas an access specifier controls what functions can access the variable. However, because member variables are defined inside classes, they can't be global, nor can they be local in the sense that a "regular" (i.e., nonmember) variable can be; a member variable must always live inside a single occurrence of an object of its class.

But although scope rules and access specifiers are similar in some ways in that they affect where a variable can be used, they aren't exactly the same. Scope defines where a variable is visible, whereas access specifiers control where a variable (or function) is accessible. That is, if you write a program that tries to read or modify a private variable from outside the class implementation, the compiler knows what you're trying to do but won't let you do it. On the other hand, if you try to access a local variable from a function where it isn't defined, the compiler just tells you it never heard of that variable, which indeed it hasn't in that context. For example, let's suppose that the local variable x defined in function abc has no existence in any other function; in that case, if you try to access a variable named x in another function, say def, where it hasn't been defined, you'll get an error message from the compiler telling you that there is no variable x in function def. However, if there is a private member variable called x defined in class ghi, and you try to access that member variable from a nonmember function, the compiler will tell you that you're trying to do something illegal. It knows which x you mean, but it won't let you access it because you don't have permission.

Susan: Are they necessary for every class?

Steve: Pretty much. The default specifier for a class is private; that is, everything you declare in a class interface before the first explicit access specifier is private. Of course, this also means that if you don't ever provide an explicit access specifier in a given class, then everything declared in that class will be private. This isn't usually very useful, because without any public functions it's hard to use a class at all.

Susan: There is a default? Then why would the default be the least useful?

Steve: To make the programmer specify what should be publicly accessible rather than have it happen automatically. In general, it's best to keep as much as possible private, to reduce the dependency of external code on the internal implementation of the class. This makes it easier to change that implementation without causing trouble for the users of the class.

Susan: OK, that makes sense now. Are there any other kinds of access specifiers or are these the only two?

Steve: Actually, there's one more called protected, that is sort of in between public and private; we'll start using it in Chapter 9.

Susan also wanted some more details about this new class scope.

Susan: How about explaining the difference between a class scope and a public access specifier?

Steve: Variables declared in a class, regardless of their access specifier, have class scope; that means that they live as long as the object that contains them. The access specifier determines who can access these variables, but does not affect their lifetime.

Of course, the constructor StockItem::StockItem(), by virtue of being a member function, has access to all member variables, so the private access specifier doesn't apply to it. We'll see later how that access specifier comes into play.

Now that we know what kind of variables the StockItem::StockItem() function deals with, its behavior isn't very mysterious: it simply initializes the member variables to 0 or the default string value (""), whichever is appropriate to their types. That's all very well, but it doesn't answer a very important question: What exactly do these member variables do? The answer is that they don't do anything by themselves; rather, they are the "raw material" the member functions use to implement the behavior that we want a StockItem to display. If you recall the discussion of interface vs. implementation, then you'll appreciate that the private member variables are also essentially part of the implementation and not part of the interface: even though they are defined in the header file, the user of the class can't access them directly.

That's why we call variables that are declared inside the class definition member variables and functions that are declared inside the class definition member functions; they "belong" to the class that we're defining. The member functions set, change, and use the values of the member variables in the course of implementing the behaviors that the StockItem class interface definition promises.[13]

[13] The special status of member functions and variables as implementation aids explains why access specifiers such as public are applicable only to data or functions declared in a class, since the purpose of access specifiers is to control "outside" access to variables and functions used to implement a class. You can't apply access specifiers to native types, because the way these types are implemented is not accessible to the programmer.

Susan wasn't buying all this malarkey about member variables without some further explanation. Here's how the discussion went:

Susan: What do m_InStock and m_Price and the others actually do? It seems we are missing a verb here.

Steve: They don't do anything by themselves. They are the member variables used to store the count and price of the goods described by a StockItem object, respectively. In the default constructor, they are both set to 0, indicating a StockItem with no content. This is the equivalent of the value 0 that is used to initialize statically allocated numeric variables and is used in the same way; that is, any StockItem that is created without a value is set to this empty state. Notice that this doesn't apply only to statically allocated StockItems, but to all StockItems; this is an example where a user-defined type is superior to a native type. That is, we don't have to worry about having an uninitialized StockItem, because the default constructor ensures that every StockItem is set to a known value when it is created.

Susan: Ugh. Don't remind me about uninitialized variables. Okay, that makes more sense now.

So much for the "high-altitude" description of what a class does. Now let's get back to the details that make it work, starting with a little puzzle: figuring out where the StockItem::StockItem() function is used in the test program in Figure 6.1 on page 303. Believe it or not, this constructor is actually used in that program; to be exact, the line StockItem soup; calls it. Remember that the basic idea of constructing a class is to add data types to the language that aren't available "out of the box". One of the functions that we have to help the compiler with is initialization; a main purpose for the StockItem::StockItem() constructor is to initialize variables of the StockItem type that aren't explicitly initialized. That's why it's called a default constructor.

Susan didn't immediately cotton to the idea of calling a default constructor by simply defining a variable of that class.

Susan: Sure, defining an object is simple if you don't lose your mind defining the classes first.

Steve: It is simple for the application programmer (the user of the class). We're doing the hard part so he can just use the objects without having to worry about any of this stuff.

Susan: Huh? Isn't the "user of the class" always the same as the "writer of the class"?

Steve: Not necessarily. You've been using strings (and Vecs, for that matter) for some time now without having to be concerned about how they work. This is not unusual.

Susan: Yeah, but if you are a programmer you will be a class writer, not just a user.

Steve: Probably not with respect to all classes. You may very well write your own application-specific classes but use existing ones for all of the low-level stuff like Vecs, strings, etc.

That's one reason why we separate interfaces and implementations: so that not everyone who uses our classes has to know exactly how they are implemented.

You should generally write a default constructor for every class you define, to guarantee the state of any "default constructed" variable. If you don't declare a default constructor, the compiler will supply one for you; however, since it doesn't know much about your class, it won't be able to guarantee very much about the initial state of one of your variables. In fact, all the native types will be left in a random state, as though they were declared but not initialized; this is an undesirable condition, as we've already seen in another context. The moral is that you should define your own default constructor. As you can see from our example, it's not much work.

So why did I say "generally", rather than "always"? Because there are some times when you don't want to allow an object to be created unless the "real" data for it are available. As with the copy constructor, the compiler will generate a default constructor for you automatically if you don't declare one yourself. If you want to make it impossible to create an object via that compiler-generated default constructor, you can declare a private default constructor that will cause a compiler error in any user code that tries to define an object of that class without specifying an initial value. You don't have to implement this constructor, because a program that tries to use it won't compile.

Susan thought that the idea of having to define a default constructor for each class was a bit off the wall.

Susan: When you say that "you should define one of these (default constructors) for every class you define..." my question is how? What are you talking about? I thought a default meant just that, it was a default, you don't have to do anything with it, it is set to a preassigned value.

Steve: It's true that the class user doesn't have to do anything with the default constructors. However, the class writer (that's us, in this case) has to define the default constructor so that when the class user defines an object without initializing it, the new object has a reasonable state for an "empty" object. This prevents problems like those caused by uninitialized variables of native types.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset