类是一种将抽象转换为用户定义类型的C++工具,它将数据和数据处理组合成一个整体。
比如股票类,首先要考虑如何表示股票。可以将某人持有的股票当成一个基本单元,数据包括他持有股票的数量,哪个公司的股票,还有最初购买的价格和购买的时间,股票的处理有增持,卖出,更新价格等。
类一般由类声明和类方法定义两部分组成,
类声明:以数据成员的方式描述部分,以成员函数的方式描述公有接口;
类方法定义:描述如何实现类成员函数。
可以这么说,类声明就是画个饼,类方法就是去实现这个饼。
一般将类声明放在头文件中,就是.h文件,将类的实现放在源文件中,就是.cpp文件。
下面是stock类的类声明
#ifndef STOCK_H
#define STOCK_H
#include
using namespace std;
class stock
{
private:
string company; //股票发行的公司
long shares; //持有股票数量
double share_val; //每股的价格
double total_val; //持股的总金额
void set_tot() {total_val = shares * share_val;}
public:
void acquire(const string& co, long n, double pr);
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
void show();
};
#endif // STOCK_H
stock是这个类的类型名,我们可以声明stock类型的变量,也称为对象或实例,每个对象都表示一支股票,可以通过下面的声明创建两个stock对象
stock wang;
stock li;
private和public关键字是C++中新出现的,它们用来描述对类成员的访问权限。private表示类的私有部分,public表示类的公有部分。使用类对象的程序可以直接访问类的公有部分;但类的私有部分只能通过类的成员函数来访问。
类成员函数的定义与常规函数的定义非常类似,有函数头、函数体,也可以有返回类型和参数。区别在于,定义类的成员函数需要使用类的作用域解析运算符::来标示函数所属的类,类的成员函数可以访问类的私有成员数据。
void stock::update(double price)
这表示update()函数是stock类的成员函数。
类的成员函数,也就是类的实现代码如下
#include "stock.h"
#include
using namespace std;
//acquire()函数管理对某个公司股票首次购买,公司名称,购买数量,每股单价
void stock::acquire(const string& co, long n, double pr)
{
company = co;
if(n<0)
{
cout << "number of shares can't be nagative; ";
cout << 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)
{
cout << "number of shares purchased can't be nagative; ";
cout << " Transaction is aborted.\n";
}
else
{
shares += num;
share_val = price;
set_tot();
}
}
//减持股票,减持的数量,和当时的价格
void stock::sell(long num, double price)
{
if(num < 0)
{
cout << "number of shares sold can't be nagative; ";
cout << " 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()
{
cout << "Company: " << company << " Shares: " << shares << endl;
cout << "Share price: " << share_val << " Total Worth: " << total_val << endl;
}
公有属性的函数没什么好说的,看代码和注释就看的明白。里面有个特别一点的函数set_tot()函数,是计算股票总资产的,这个函数是类的私有成员函数,代码中每个公有成员函数都有使用它,方法都是一样的,就没必要写重复的代码,这样既减少了重复代码,有节省了空间。
经过头文件和源文件的编写,stock类已经创建好了,可以开始使用了,先创建两个类对象,kate与joe.
stock kate, joe;
如何使用对象的成员函数
kate.show();
joe.show();
第一句调用kate对象的show()成员函数,这意味着此时的shares代表的是kate.shares,此时的share_val 代表的是kate.share_val 。第二句同理。
所创建的每个新对象都有自己的存储空间,用于存储其内部变量和类成员,但是同一个类的所有对象共享同一组类方法,也就是共用同一套类成员函数。kate和joe都是stock对象,kate.shares占一块内存,joe.shares占另外一块内存,它们是不一样的东西。
C++的目标是让使用类与使用基本的类型尽可能相同。要创建类对象,可以声明类变量,也可以使用new为类对象分配存储空间,可以将对象作为函数的参数和返回值,也可以将一个对象赋值给另一个对象。
下面是类的使用代码示例,创建一个stock对象 dreamer
#include
#include "stock.h"
using namespace std;
int main()
{
stock dreamer;
dreamer.acquire("hengda", 20, 12.50); //dreamer以每股12.5元的价格买了20股恒大的股票
dreamer.show();
dreamer.buy(15, 18.25);//dreamer又以18.25元的价格增持了15股恒大的股票
dreamer.show();
dreamer.sell(400,20);//dreamer想以20元的价格卖掉400股,好像在做梦,都没有那么多股票
dreamer.show();
dreamer.buy(20000, 20);//dreamer突然有很多钱了,以每股20元的价格,一下子买了20000股恒大的股票
dreamer.show();
dreamer.sell(20000, 0.15);//完蛋了,恒大破产了,股票跌到1毛五,赶紧抛吧
dreamer.show(); //变成穷光蛋了吧
return 0;
}
输出结果
Company: hengda Shares: 20
Share price: 12.5 Total Worth: 250
Company: hengda Shares: 35
Share price: 18.25 Total Worth: 638.75
you can't sell more than you have! transaction is aborted.
Company: hengda Shares: 35
Share price: 18.25 Total Worth: 638.75
Company: hengda Shares: 20035
Share price: 20 Total Worth: 400700
Company: hengda Shares: 35
Share price: 0.15 Total Worth: 5.25
其实上面创建的stock类还不完善,还需要为它添加构造函数与析构函数,为什么?
C++的目标是让使用类跟使用基本类型一样简单,但是到目前为止,我们还不能像初始化int变量那样初始化stock对象,也就是说常规的初始化语法不适用于stock类。
如int变量和结构体变量都可以这样初始化
int year = 2023; //int变量初始化
struct thing
{
char* pn;
int m;
};
thing amabob = {"wodget", -23};//结构体变量初始化
类对象可不能这么初始化
stock dreamer = {"hengda", 250, 2.5};
类对象不能这么初始化,是因为数据部分的访问是私有的,这意味着程序不能直接访问数据成员。程序只能通过成员函数来访问数据成员,因此需要设计合适的成员函数,才能将对象初始化,这个合适的函数就是构造函数。
构造函数就是用于构造新对象,将值赋给它们的数据成员。
构造函数没有返回值,构造函数的名字与类名字一样;
还是延续前面的stock类,为其创建一个构造函数,需要为stock对象提供3个值,因此为构造函数提供3个参数,如果指向设置company成员,将其他的值设为0
stock::stock(const string& co, long n, double pr)
{
company = co;
if(n<0)
{
cout << "number of shares can't be nagative; ";
cout << company << " shares set to 0.\n";
shares = 0;
}
else
{
shares = n;
}
share_val = pr;
set_tot();
}
这个构造函数的代码与acquire()函数的代码相同,区别在于,程序声明对象的时候会自动调用构造函数。这个自动调用也不是那么的自动,还是要做一些工作。可以显式调用也可以隐式调用
显式调用构造函数
#include
#include "stock.h"
using namespace std;
int main()
{
stock dreamer = stock("apple",10, 12); //显式调用构造函数,将dreamer对象初始化
dreamer.show();
return 0;
}
输出结果
Company: apple Shares: 10
Share price: 12 Total Worth: 120
隐式调用构造函数,就是在对象后面直接把参数贴上去
#include
#include "stock.h"
using namespace std;
int main()
{
stock dreamer("apple",10, 12); //隐式调用构造函数,将dreamer对象初始化
dreamer.show();
return 0;
}
输出结果是一样的 。
如果程序没有写构造函数,C++的编译器会提供一个默认的构造函数,里面没有初始的数据。
stock::stock() {}
当且仅当程序没有写任何构造函数,编译器才会提供一个默认构造函数,否则,就得自己写一个默认构造函数。
定义了常规构造函数和默认构造函数之后,声明一个类对象之后,如果没有调用常规构造函数,程序或自动调用默认构造函数
#include
#include "stock.h"
using namespace std;
int main()
{
stock dreamer; //程序调用默认构造函数
dreamer.show();
return 0;
}
输出结果
Company: no name Shares: 0
Share price: 0 Total Worth: 0
七、类的析构函数
用构造函数创建对象后,程序负责跟踪该对象,直到过期。对象过期时,程序将自动调用一个特殊的成员函数,这个函数就是析构函数,析构函数完成清理工作,如果构造函数使用new来分配内存,那么析构函数将使用delete释放内存。
西沟函数长这个样子
~stock();
由于析构函数不用承担任何重要的工作,里面可以不放代码
stock::~stock()
{
}
如果你想看看西沟函数是否运行,可以在析构函数里面放一个打印语句
stock::stock()
{
cout << "using ~stock().\n"
}
如果我们调用stock类对象的show函数,show()函数没有任何参数,如果定义的类对象是const的,那么有的编译器在调用show时会报错,因为编译器不知道show函数是否会修改调用的对象,保险的做法是在不会改变调用对象的函数后面加个const,如下所示
//显示股票的信息
void stock::show() const
{
cout << "Company: " << company << " Shares: " << shares << endl;
cout << "Share price: " << share_val << " Total Worth: " << total_val << endl;
}
这样就保证了show函数不会修改调用的对象。
对于stock类,还有很多工作要做,到目前为止,每个类成员函数都只设计一个对象,即调用他的对象,但是有时候成员函数可能涉及到两个对象,在这种情况下需要使用C++的this指针。
虽然stock类声明可以显示数据,但它缺乏分析能力,例如从show()输出我们可以知道持有的哪一只股票价格最高,但是由于程序无法直接访问total_val,因此无法做出判断。要让程序知道存储的数据,最直接的方式是让成员函数返回一个值,为此,通常使用内联代码
class stock
{
private:
...
double total_val;
...
public:
double total() const { return total_val;}
};
就直接程序访问而言,上述定义实际上是使total_val只读,也就是输可以使用方法total_val()来获得total_val的值,但这个类没有专门用于重新设置total_val值的方法。
const stock& stock::topval(const stock& s) const
{
if(s.total_val > total_val)
return s;
else
return *this;
}
C++中通过引用this指针来解决这一问题,this指针指向用来调用成员函数的对象,this被作为隐藏参数传递给成员函数,这样调用stock1.topval(stock2)将this设置为stock1对象的地址,同样调用stock2.topval(stock1),将this设置为stock2对象的地址。