第10章 对象和类
10.2 抽象和类
接口描述了用户如何初始化、更新和显示数据
C++中,用户定义类型指的是实现抽象接口的类设计
10.2.1 类型
指定基本类型完成以下工作:
决定数据对象需要的内存数量;
决定如何解释内存中的位;
决定可使用数据对象执行的操作或方法;
10.2.2 C++中的类
1.类是一种将抽象转换为用户定义类型的C++工具,它将数据表示和操纵数据的方法合成一个包。
2.接下来定义类,类规范由两个部分组成:
类声明:以数据成员的方式描述数据部分,以成员函数(或方法)的方式描述公有接口。
类方法定义:描述如何实现类成员函数
3. C++中将接口(类定义)放在头文件中,并将实现(类方法的代码)放在源代码文件中。
首先看一下类定义
//stock00.h
#ifndef STOCK00_H_
#define STOCK00_H_
#include <string>
class Stock //class declaration
{
private:
std::string company;
long shares;
double share_val;
double total_val;
void set_tot(){total_val=shares*share_val};
public:
void acquire(const std::string & co,long n,double pr);
void buy(long num,double price);
void sell(long num,double price);
void update(double price);
void show();
}; //note semicolon at the end
#endif
代码说明:
关键词class 标识类定义
类名称Stock成为这个用户定义的类型的名称
关键字private标识只能通过公共成员访问的类成员(数据隐藏)
关键字public标识组成类的公共接口的类成员(抽象)
类成员可以是数据类型,也可以是函数
由于隐藏数据是面向对象编程主要的目标之一,因此数据项通常放在私有部分,组成类的接口的成员函数放在公有部分;
公有方法可以使用私有部分中的数据,也可以使用私有成员函数来处理不属于公有接口的实现细节。
不必在类声明中使用关键字private,因为这是类对象的默认访问控制
10.2.3 实现类成员函数
类成员函数具有两个特殊的特征:
定义成员函数时,使用作用域解析运算符(::)来标识函数所属的类;
类方法可以访问类的private组件
成员函数的编写:
// stock00.cpp -- implementing the Stock class
// version 00
#include <iostream>
#include "stock00.h"
void Stock::acquire(const std::string & co, long n, double pr)
{
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();
}
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'
<< " Share Price: $" << share_val
<< " Total Worth: $" << total_val << '\n';
}
10.2.4 使用类
创建并使用类对象
// usestok0.cpp -- the client program
// compile with stock.cpp
#include <iostream>
#include "stock00.h"
int main()
{
Stock fluffy_the_cat;
fluffy_the_cat.acquire("NanoSmart", 20, 12.50);
fluffy_the_cat.show();
fluffy_the_cat.buy(15, 18.125);
fluffy_the_cat.show();
fluffy_the_cat.sell(400, 20.00);
fluffy_the_cat.show();
fluffy_the_cat.buy(300000,40.125);
fluffy_the_cat.show();
fluffy_the_cat.sell(300000,0.125);
fluffy_the_cat.show();
// std::cin.get();
return 0;
}
10.3 类的构造函数和析构函数
利用类的构造函数对对象进行初始化。
10.3.1 声明和定义构造函数
创建Stock的构造函数。由于需要为Stock对象提供3个值,因此应为构造函数提供3个参数。
一是可以使用默认参数完成:
Stock(const string &co,long n=0,double pr=0.0);
二是利用构造函数定义:
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();
}
注意:构造函数的参数表示的不是类成员,而是赋给类成员的值。
10.3.2 使用构造函数
C++提供了两种使用构造函数来初始化对象的方法。
第一种是显示地调用构造函数:
Stock food=Stock("world Cabbage",250,1.25);
第二种是隐式地调用构造函数:
Stock garmet("Furry Mason",50,2.5);
下面是将构造函数与new一起使用的方法:
Stock *pstock=new Stock("Electroshock Games",18,19.0);
10.3.4 析构函数
利用构造函数创建对象后,程序负责跟踪该对象。对象过期时,程序将自动调用析构函数完成清理工作。特别是在构造函数使用new分配内存时,析构函数则使用delete来释放内存。当构造函数没有使用new,析构函数实际上没有需要完成的任务。
析构函数没有参数,因此Stock析构函数的原型必须如下:
~Stock();
则Stock的析构函数可以编写为:
Stock::~Stock()
{
}
10.3.5 改写原有code
将构造函数和析构函数加入到类和方法的定义中,则原先的stock00改为stock10
1)头文件
#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);
~Stock(); // noisy destructor
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
void show();
};
#endif
2)实现文件
// stock1.cpp ?Stock class implementation with constructors, destructor added
#include <iostream>
#include "stock10.h"
// constructors (verbose versions)
Stock::Stock() // default constructor
{
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();
}
// class destructor
Stock::~Stock() // verbose class destructor
{
std::cout << "Bye, " << company << "!\n";
}
// 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)
{
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;
// set format to #.###
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;
// set format to #.##
cout.precision(2);
cout << " Total Worth: $" << total_val << '\n';
// restore original format
cout.setf(orig, ios_base::floatfield);
cout.precision(prec);
}
客户文件:
// usestok1.cpp -- using the Stock class
// compile with stock10.cpp
#include <iostream>
#include "stock10.h"
int main()
{
{
using std::cout;
cout << "Using constructors to create new objects\n";
Stock stock1("NanoSmart", 12, 20.0); // syntax 1
stock1.show();
Stock stock2 = Stock ("Boffo Objects", 2, 2.0); // syntax 2
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); // temp object
cout << "Revised stock1:\n";
stock1.show();
cout << "Done\n";
}
// std::cin.get();
return 0;
}
10.4 this指针
当类成员函数涉及到两个对象时,在这种情况下需要使用C++的this指针
声明:
const Stock & topval(const Stock & s) const;
成员函数
const Stock & Stock::topval(const Stock & s) const
{
if (s.total_val > total_val)
return s;
else
return *this;
}
10.5 对象数组
用户可以创建同一个类的多个对象。声明对象数组的方法与声明标准类型数组相同:
Stock mystuff[4]; //creates an array of 4 Stock objects
当程序创建未被显式初始化的类对象时,总是调用默认构造函数。每个元素(mystuff[0]、mystuff[1]等)但是Stock对象,可以使用Stock方法:
mystuff[0].update();
mystuff[3].show();
const Stock *tops=mystuff[2].topval(mystuff[1]);
可以用构造函数来初始化数组元素。在这种情况下,必须为每个元素调用构造函数:
const int STKS=10;
Stock stocks[STKS]={
Stock("NA",12.5,20),
Stock("NA7",12.5,20),
Stock("PNA",19.5,20),
Stock("NAU",12.5,201)
};
如果声明只初始化了数组的部分元素,那么余下的元素将使用默认构造函数进行初始化。