Cpp Chapter 10: Objects and Classes Part2
10.2.4 Using classes
Following exapmle uses the class definition and implementation written in previous files:
// usestok0.cpp -- the client program // compiler with stock00.cpp #include <iostream> #include "stock00.h" int main() { Stock s; s.acquire("NanoSmart", 20, 12.5); s.show(); s.buy(15, 18.125); s.show(); s.sell(400, 20.00); s.show(); s.buy(300000, 40.125); s.show(); s.sell(300000, 0.125); s.show(); return 0; }
) The code below is a client:
client: a program that uses the class
server: the class declaration and the class methods
10.2.5 Changing the implementation
Output formatting features for cout:
avoid e-notation
std::ios_base::fmtflags orig = std::cout.setf(std::ios_base::fixed, ios_base::floatfield); // set the format while store original format in orig ... std::cout.setf(orig, std::ios_base::floatfield) // reset to original format
set output precision
std::streamsize prec = std::cout.precision(3); // save preceding precision in prec
...
std::cout.precision(prec);
10.2.6 Reviewing our story to date
) Use member functions by applying membership operator(.) to objects: object.func();
10.3 Class Constructors and Destuctors
To accomplish initialization while declaration for class objects, C++ provides special member functions called class constructors
10.3.1 Declaring and defining constructors
No return type, prototype goes in the public section:
// constructor prototype with default arguments
Stock(const string & co, long n = 0, double pr = 0.0);
Next comes the definition of the constructor in separate file:
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();
}
) A program automatically invokes the constructor when it declares a object
) Parameters of the constructor should not share name with the member data.
10.3.2 Using constructors
There are explicit and implicit way to initialize objects with constructor:
Stock food = Stock("World Cabbage", 250, 1.25); // explicit
Stock garment("Furry Mason", 50, 2.5); // implicit
Whether calling the constructor function Stock() explicitly or use parentheses following an object‘s declaration are equivalent.
10.3.3 Default constructors
) If you don‘t provide any constructors, C++ automatically supplies a default constructor:
Stock::Stock() {}
When you initialize an object without initialization, you are just leaving all of its member data uninitialized:
Stock s; // values of s member data are uninitialized
) You could provide you own version of default constructor by 2 ways:
1 default arguments: set all arguments to be default arguments in the constructor:
Stock(const string & co = "Error", int n = 0, double pr = 0.0);
2 use function overloading to define a second constructor with no arguments:
Stock::Stock()
{
company = "no name";
shares = 0;
share_val = 0.0;
total_val = 0.0;
}
) If you have provided a nondefault constructor without providing a default one, declaration like this is an error:
Stock s; // not possible with current constructor
So it is usually correct to provide a default constructor with a nondefault one
) Examples of using constructors:
Stock first; // calls default constructor implicitly
Stock first = Stock(); // calls default constructor explicitly
Stock *prelief = new Stock; // calls default constructor implicitly
Stock first("Concrete Conglomerate"); // calls constructor
Stock second(); // !!! declares a function!!!
Stock third; // calls default constructor
When you implicitly call the default constructor, you don‘t use parentheses(difference between line 5 and line 6 in the code above)
10.3.4 Destructors
) A destructor is called automatically when a class object expires. A destructor has a special name: formed by the class name preceded by a tilde(~):
~Stock();
No return value, no declared type and no arguments for destructors.
) You have 2 ways of having a destructor:
The compiler generates an implicit, do-nothing destructor.
- You provide definition for the destructor, no matter do-nothing type:
Stock::~Stock()
{
}
or do-something type:
Stock::~Stock()
{
cout << "Bye, " << company << "!\n";
}
10.3.5 Improving the stock class
) Major changes to the three files:
1 Adds constructors and destructors, removing unnecessary acquire() function
2 Format the output in Stock::show()
3 Refine the object-declaration syntax
void Stock::buy(long num, double price)
{
if (num < 0)
{
std::cout << "Number of shares purchased can‘t be negative. " << "Transaction is aborted.\n";
}
else
{
shares += num;
share_val = price;
set_tot();
}
}
void Stock::sell(long num, double price)
{
using std::cout;
if (num < 0)
{
cout << "Number of shares sold can‘t be negative. Transaction is aborted.\n";
}
else if (num > shares)
{
cout << "You can‘t sell more than you have! Transaction is aborted.\n";
}
else
{
shares -= num;
share_val = price;
set_tot();
}
}
void Stock::update(double price)
{
share_val = price;
set_tot();
}
void Stock::show()
{
std::cout << "Company: " << company << " Shares: " << shares << "\n";
std::cout << " Shares Price: $" << share_val << " Total Worth: $" << total_val << "\n";
}
// stock10.cpp -- Stock class with constructors, destructor added
#include <iostream>
#include "stock10.h"
Stock::Stock()
{
std::cout << "Default constructor called\n";
company = "no name";
shares = 0;
share_val = 0.0;
total_val = 0.0;
}
Stock::Stock(const std::string & co, long n, double pr)
{
std::cout << "Constructor using " << co << " called\n";
company = co;
if (n < 0)
{
std::cout << "Number of shares can‘t be negative; " << company << " shares set to 0.\n";
shares = 0;
}
else
shares = n;
share_val = pr;
set_tot();
}
Stock::~Stock()
{
std::cout << "Bye, " << company << "!\n";
}
void Stock::buy(long num, double price)
{
if (num < 0)
{
std::cout << "Number of shares purchased can‘t be negative. " << "Transaction is aborted.\n";
}
else
{
shares += num;
share_val = price;
set_tot();
}
}
void Stock::sell(long num, double price)
{
using std::cout;
if (num < 0)
{
cout << "Number of shares sold can‘t be negative. Transaction is aborted.\n";
}
else if (num > shares)
{
cout << "You can‘t sell more than you have! Transaction is aborted.\n";
}
else
{
shares -= num;
share_val = price;
set_tot();
}
}
void Stock::update(double price)
{
share_val = price;
set_tot();
}
void Stock::show()
{
using std::cout;
using std::ios_base;
ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield);
std::streamsize prec = cout.precision(3);
cout << "Company: " << company << " Shares: " << shares << "\n";
cout << " Share price: $" << share_val;
cout.precision(2);
cout << " Total Worth: $" << total_val << "\n";
cout.setf(orig, ios_base::floatfield);
cout.precision(prec);
}
// usestock1.cpp -- using the Stock class
// compile with stock10.cpp
#include <iostream>
#include "stock10.h"
int main()
{
{ // 1111111
using std::cout;
cout << "Using constructors to create new objects\n";
Stock stock1("NanoSmart", 12, 20.0);
stock1.show();
Stock stock2 = Stock("Boffo Objects", 2, 2.0);
stock2.show();
cout << "Assigning stock1 to stock2:\n";
stock2 = stock1;
cout << "Listing stock1 and stock2:\n";
stock1.show();
stock2.show();
cout << "Using a constructor to reset an object\n";
stock1 = Stock("Nifty Foods", 10, 50.0);
cout << "Revised stock1:\n";
stock1.show();
cout << "Done\n";
} // 1111111
return 0;
}
The result of the code:
) Noteworthy:
1 Extra braces were used(in usestok1.cpp where 1111111 occurs). In windowing environment, the window would close before the last two destructor calls, preventing you from seeing the last two "Bye, NanoSmart!" and "Bye, Nifty Foods!"
2 The following code:
Stock stock2 = Stock("Boffo Objects", 2, 2.0);
C++ actually has two ways to execute this: One is to let it behave like the following syntax:
Stock stock2("Boffo Objects", 2, 2.0);
The second is to create a temporary object which is then copied to stock2, which will cause the destructor call for temporary object. As a result, you will see output of "Bye, Boffo Objects" after the compiler calls the constructor, (which is kind of weird for me lol)
3 You could do object assignment:
stock2 = stock1;
4 Analyze following code:
stock1 = Stock("Nifty Foods", 10, 50.0);
stock1 already exists, so here the compiler create a temporary object of Stock which is later copied to stock1 to rewrite the contents of stock1. In this way, you could also use constructors to reassign objects.
5 In the output this could be observed:
Bye, NanoSmart!
Bye, Nifty Foods!
Remember that those automatic variables go on a stack which follows the principle of LIFO(last in first out), so the destructor is also called to delete them in the LIFO order
6 Choose initialization rather than assignment if possible(more efficient):
Stock stock2 = Stock("thing", 2, 2.0); // initialization invoked, doesn‘t create temporary objects
stock1 = Stock("thing2", 3, 2.0); // assignment invoked, create temporary objects
) You could also use list initialization with objects(those with braces):
Stock hot_tip = {"Derivatives Plus Plus", 100, 45.0};
Stock temp {};
const member functions
const Stock land = Stock("thing");
land.show();
The compiler would reject the second line because Stock::shoe() doesn‘t promise not to alter the data, where land is a const object. Solution is to place the const keyword after the function parentheses:
void show() const; // prototyp3
void stock::show() const // providing definition
{
...
}
Class functions declared and defined in this way are called const member functions.
10.4 Knowing your objects: the this pointer
) Consider defining a member function that looks at two Stock objects and return the reference to the larger of the two:
const Stock & topval(const Stock & s) const;
The first const says that it returns a reference to a const object, the second const shows that the argument object‘s value can‘t be altered, the third const promises that the function would not alter the data of the invoking object.
The function accesses one object implicitly(the invoking object) and one object explicitly(the argument object).
) A special pointer this is used to access the invoking object. The this pointer points to the object used to invoke a member function. To access the invoking object‘s members, use operator "->":
cout << this->total_val; // use -> to access data from pointer to structures or classes
When returning a reference to the invoking object, **return (*this)** as the invoking object. Complete definition of the comparison member function:
const Stock & Stock::topval(const Stock & s) const
{
if (s.total_val > total_val)
return s;
else
return *this;
}
Here comes revised version of class declaration and implementation files:
#ifndef STOCK20_H_INCLUDED
#define STOCK20_H_INCLUDED
#include <string>
class Stock
{
private:
std::string company;
long shares;
double share_val;
double total_val;
void set_tot() {total_val = shares * share_val; }
public:
Stock(); // default constructor
Stock(const std::string & co, long n = 0, double pr = 0.0); // constructor
~Stock(); // destructor
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
void show();
const Stock & topval(const Stock & s) const;
};
#endif // STOCK20_H_INCLUDED
#ifndef STOCK20_H_INCLUDED
#define STOCK20_H_INCLUDED
#include <string>
class Stock
{
private:
std::string company;
long shares;
double share_val;
double total_val;
void set_tot() {total_val = shares * share_val; }
public:
Stock(); // default constructor
Stock(const std::string & co, long n = 0, double pr = 0.0); // constructor
~Stock(); // destructor
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
void show();
const Stock & topval(const Stock & s) const;
};
#endif // STOCK20_H_INCLUDED
Cpp Chapter 10: Objects and Classes Part2