第十章 对象和类(中)
10.4 this 指针
到目前为止,每个类成员函数都只涉及一个对象,即调用它的对象。但有时候方法可能设计到多个对象,这是就需要使用this
指针。依然以之前的股票模型为例。我们可以使用show()
来输出手中持有股票价格最高的股票,但是由于程序无法直接访问total_val()
,因此无法作出判断。要让程序知道储存的数据,最直接的方法是让方法返回一个值。比如:
class Stock
{
private:
...
double total_val;
...
public:
double total() const {return total_val;}
...
};
这样我们可以查看到这个对象的总价值,但是如果我们要比较两个对象的股价,用这样的方法会有点麻烦。下面将介绍使用this
指针来完成这样的操作。
程序 10.7 stock20.h
// stock20.h -- augmented version
#ifndef STOCK20_H_
#define STOCK20_H_
#include <string>
class Stock
{
private:
std::string company;
int shares;
double share_val;
double total_val;
void set_tot() { total_val = shares * share_val; }
public:
Stock(/* args */);
Stock(const std::string &co, long n = 0, double pr = 0.0);
~Stock();
void buy(long num, double price);
void sell(long num, double price);
void update(double prive);
void show() const;
const Stock &topval(const Stock &s) const;
};
#endif
程序 10.8 stock20.cpp
// stock20.cpp -- augmented version
#include <iostream>
#include "stock20.h"
// constructors
Stock::Stock()
{
company = "no name";
shares = 0;
share_val = 0.0;
total_val = 0.0;
}
Stock::Stock(const std::string &co, long n, double pr)
{
company = co;
if (n < 0)
{
std::cout << "Number os shares can't be negative;"
<< company << " shares set to 0.\n";
shares = 0;
}
else
shares = n;
share_val = pr;
set_tot();
}
// class destructor
Stock::~Stock() {}
// other methods
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)
{
if (num < 0)
{
std::cout << "Number of shares sold can't be negative. "
<< "Transaction is aborted.\n";
}
else if (num > shares)
{
std::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() const
{
using std::cout;
using std::ios_base;
// set format to #.###
ios_base::fmtflags orig =
cout.setf(ios_base::fixed, ios_base::floatfield);
std::streamsize prec = cout.precision(3);
cout << "Company: " << company
<< "\n Shares: " << shares
<< "\n Share Price: $" << share_val;
// set format to #.##
cout.precision(2);
cout << "\n Total Worth: $" << total_val << '\n';
// restore original format
cout.setf(orig, ios_base::floatfield);
cout.precision(prec);
}
const Stock &Stock::topval(const Stock &s) const
{
if (s.total_val > total_val)
return s;
else
return *this;
}
为了验证this
指针是否有用,我们需要再一个包含对象数组的程序中使用着用新方法。
10.5 对象数组
像变量一样,对象也可以创建一个数组。声明方法与之前的标准数组一样。
Stock mystuff[4]; // creates an array of 4 Stock objects
下面用程序来做演示:
程序 10.9 usestock2.cpp
// usestock2.cpp -- useing the stock class
// compile with stock20.cpp
#include <iostream>
#include "stock20.h"
const int STKS = 4;
int main(int argc, char const *argv[])
{
// create an array of initialized objects
Stock stocks[STKS] =
{
Stock("NanoSmart", 12, 20.0),
Stock("Boffo Objects", 200, 2.0),
Stock("Monolithic Obelisks", 130, 3.25),
Stock("Fleep Enterprises", 60, 3.5)};
std::cout << "Stock holdings:\n";
int st;
for (st = 0; st < STKS; st++)
stocks[st].show();
// set pointer to first element
const Stock *top = &stocks[0];
for (st = 1; st < STKS; st++)
top = &top->topval(stocks[st]);
// now top points to the most valuable holding
std::cout << "\nMost valueable holding: \n";
top->show();
return 0;
}
程序输出:
Company: NanoSmart
Shares: 12
Share Price: $20.000
Total Worth: $240.00
Company: Boffo Objects
Shares: 200
Share Price: $2.000
Total Worth: $400.00
Company: Monolithic Obelisks
Shares: 130
Share Price: $3.250
Total Worth: $422.50
Company: Fleep Enterprises
Shares: 60
Share Price: $3.500
Total Worth: $210.00
Most valueable holding:
Company: Monolithic Obelisks
Shares: 130
Share Price: $3.250
程序说明:
在上面的函数中,对象数组的概念比较简单,这里就不加说明了。重点来讲一下this
指针。this
指针出现在程序stock20.cpp
中的topval()
函数中
const Stock &Stock::topval(const Stock &s) const
{
if (s.total_val > total_val)
return s;
else
return *this;
}
这个函数的定义中,返回值是Stock
类的地址,参数是一个Stock
类对象的引用。而最后的返回值*this
是调用topval()
函数的这个对象的地址。
10.6 类作用域
在类中定义名称(如类数据成员名和类成员函数名)的作用域都是整个类,作用域为整个类的名称旨在该类中是已知的,在类外是不可知的。
10.6.1 作用域为类的常量
有时候,是符号常用的作用域为类很有用。例如,类声明可能使用字面值 12 来定义数组的长度,由于该常量对于所有对象来说都是相同的,因此创建一个由所有对象共享的常量是一个不错的主义。于是进行了下列的操作:
class Bakery
{
private:
const int Months = 12; // declare a constant? FAILS
double cost [Months];
...
}
但是这样是行不通的,因为声明类只是描述了对象的形式,并没有创建对象。因此,在创建对象前,将没有用于储存值的空间。然而,有两种方式可以实现这个目标,兵器效果相同。
第一种是在类中声明一个枚举。在类声明中声明的枚举的作用域为整个类,因此可以用枚举为整型常量提供作用域为整个类的符号名称。即进行下面的代码操作:
class Bakery
{
private:
enum {Month = 12};
double cost [Months];
...
}
上面枚举不对创建类数据成员,对于这个类而言,`Months`只是一个符号名称,在作用域为整个类的代码中遇到它,编译器将用30来替换他。
另外一种方式在类中定义常量是使用`static`关键词:
```cpp
class Bakery
{
private:
static const int Month = 12;
double cost [Month];
}
这里创建了一个名为 Month
的常量,该常量将与其他静态常量储存在一起,而不是储存在对象中。因此只有一个Month
常量,被所有的对象共享。
10.6.2 作用域内枚举
使用传统的枚举方法如:
enum egg{Small, Medium, Large, Jumbo};
enum t_shirt{Small, Medium, Large, Jumbo};
上面的这段代码,编译器将无法通过,因为egg Small
和t_shirt Small
位于相同的作用域内,它们将发生冲突。为了避免这样的问题,我们应该使枚举量的作用域为类。
enum class egg{Small, Medium, Large, Jumbo};
enum class t_shirt{Small, Medium, Large, Jumbo};
这样,我们就可以使用作用域解析符(: