Cpp Chapter 10: Objects and Classes Part3
10.5 An array of objects
) You can define an array of objects just as an array of built-in types:
Stock mystuff[4];
Declaring without initialization requires the class Stock to have default constructor(whether generated by compiler or given by the programmer)
) The most common form of initialization is to use a comma-separated list of values enclosed in braces
Stock stocks[4] =
{
Stock("1", 1, 1),
Stock("2", 2, 2),
Stock("3", 3, 3),
Stock("4", 4, 4)
};
If you have defined multiple constructors, you could also use different constructors in different object‘s initialization
) Here is an example of object array, using predefined Stock class:
// usestok2.cpp -- using the Stock class // compile with stock20.cpp #include <iostream> #include "stocks20.h" const int STKS = 4; int main() { Stock stocks[STKS] = { Stock("NanoSmart", 12, 20.0), Stock("Boffo Objects", 200, 2.0), Stock("Monolithic Obelisks", 130, 3.25), Stock("Fleep Enterprises", 60, 6.5) }; std::cout << "Stock holdings:\n"; int st; for (st = 0; st < STKS; st++) stocks[st].show(); const Stock * top = &stocks[0]; for (st = 1; st < STKS; st++) top = &top->topval(stocks[st]); std::cout << "\nMost valuable holding:\n"; top->show(); return 0; }
10.6 Class scope
class scope
Class scope applies to names defined in a class, such as names of class data members and member functions. Names of class scope adhere to following principles:
1 Within a class declaration or a member function definition you can use an unqualified name (total_val)
2 A constructor name is recognized because it is the same with the class name
3 Otherwise, you must invoke methods with objects, using direct membership operator(.) or indirect membership operator(->)** or scope-resolution operator(::) when you use a class member name
class scope constants
The conventional way of declaring constants doesn‘t work in class scope:
class Bakery
{
private:
const int Months = 12; // FAILS
double costs[Months];
...
}
This is because that only when an object is declared will storage be allocated for member data. So if no object declared, Months won‘t exist for all Bakery members/
Instead, you have 2 choices of declaring class scope constants:
1 declare an enumeration:
class Bakery
{
private:
enum {Months = 12};
double costs[Months];
...
}
Declaraing an enumeration in this way does not create a class data member, Months is just a symbolic constant
2 using the keyword static:
class Bakery
{
private:
static const int Months = 12;
double costs[Months];
...
}
This creates a constant Months which is stored with other static variables rather than an object. In this way Months is shared by all Bakery objects
Scoped enumerations
Enumerations sharing the same names may conflict because they are in the same scope:
enum egg {Small, Medium, Large};
enum t_shirt {Small, Medium, Large};
To cope with this conflict, use scoped enumerations:
enum class egg {Small, Medium, Large};
enum class t_shirt {Small, Medium, Large};
Under theses declarations, you should use the enum name to qualify the enumerator:
egg choice = egg::Large;
t_shirt Floyd = t_shirt::Large;
10.7 Abstract data types
) Using a class is a good way to implement an abstract data type(ADT)
An ADT describes a data type in a general fashion without bringing in language or implementation details.
) Giving a stack for example:
First, a stack holds various items.
Next, a stack could do following operations:
1 create an empty stack
2 push an item to the top of the stack
3 pop an item at the top
4 check whether the stack is full
5 check whether the stack is empty
You can use long, double or int to implement the task, and you could use array or a linked list to store the items, these features show that An ADT describes a data type generally without bringing in language or implementation details
Here comes code for implementing a stack:
#ifndef STACK_H_INCLUDED
#define STACK_H_INCLUDED
typedef unsigned long Item;
class Stack
{
private:
enum {MAX = 10};
Item items[MAX];
int top;
public:
Stack();
bool isempty() const;
bool isfull() const;
bool push(const Item & item); // false if full, true otherwise
bool pop(Item & item); // pop top into item
};
#endif // STACK_H_INCLUDED
// stack.cpp -- Stack member functions
#include "stack.h"
Stack::Stack()
{
top = 0;
}
bool Stack::isempty() const
{
return top==0;
}
bool Stack::isfull() const
{
return top==MAX;
}
bool Stack::push(const Item & item)
{
if (top < MAX)
{
items[top++] = item;
return true;
}
else
return false;
}
bool Stack::pop(Item & item)
{
if (top > 0)
{
item = items[--top];
return true;
}
else
return false;
}
// stacker.cpp -- testing the Stack class
#include <iostream>
#include <cctype>
#include "stack.h"
int main()
{
using namespace std;
Stack st;
char ch;
unsigned long po;
cout << "Please enter A to add a purchase order, \nP to process a PO, or Q to quit.\n";
while (cin >> ch && toupper(ch) != ‘Q‘)
{
while (cin.get() != ‘\n‘)
continue;
if (!isalpha(ch))
{
cout << "\a";
continue;
}
switch(ch)
{
case ‘A‘:
case ‘a‘: cout << "Enter a PO number to add: ";
cin >> po;
if (st.isfull())
cout << "stack already full\n";
else
st.push(po);
break;
case ‘p‘:
case ‘P‘: if (st.isempty())
cout << "stack already empty\n";
else
{
st.pop(po);
cout << "PO #" << po << " popped\n";
}
break;
}
cout << "Please enter A to add a purchase order,\nP to process a PO, or Q to quit.\n";
}
cout << "Bye\n";
return 0;
}
In stack.h, we use the code:
typedef unsigned long Item;
which tells the compiler to treat Item as unsigned long, revealing that the implementation of stack doesn‘t rely on specific data types, you could change the long here to double, and whatever you want. The private section of the class Stack handles storage of items, while the public section of the class handles operations and interfaces.
Cpp Chapter 10: Objects and Classes Part3