this指针

到目前为止,每个类成员函数只涉及一个对象,即调用它的对象。但是我们有时候需要涉及多个对象,在这种情况下,必须使用this指针。
例如,我们要求一个函数实现比较两个股票的总价值,并选出大的那个股票。
那么这个成员函数的原型肯定是:
const Stock & topval(const Stock &s)const;
首先这肯定是const成员函数,其次它的肯定要接收一个Stock对象,返回值也必须是一个Stock对象。
那么,调用这个成员函数时,
top=stock1.topval(stock2)top=stock2.topval(stock1)是等价的。
下面我们来看看函数如何定义?

const Stock & Stock::topval(const Stock& s)const
{
if(s.total_val>total_val)
{
return s;
}
else
return ???;
}

有没有发现问题?函数无法返回调用这个函数的对象本身。C++为了解决这个问题,提供了this指针。

  • this指针是什么?

    这是一种特殊指针,它指向调用函数的对象,也就是说this指针的值是调用对象的地址。
    每个成员函数都有一个this指针,*this就算调用对象。
    如果是const成员函数,那么我们无法用this指针修改调用对象。

则上面的函数应该这样定义:

const Stock & Stock::topval(const Stock& s)const
{
if(s.total_val>total_val)
{
return s;
}
else
return *this;
}

这样一来,我们再次更新了我们的Stock

Stock类的最终版本

//类4.h
#ifndef aa
#define aa

#include<iostream>
#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();//默认构造函数
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 price);//更新股票价格
void show() const;//显示信息
const Stock & topval(const Stock & s)const;//返回价值更高的股票
};


#endif

//类4.cpp
#include<iostream>
#include"类4.h"
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<<"数量不能是负数"
<<company<<"的股票数量将设置成0\n";
shares=0;
}
else
{
shares=n;
}
share_val=pr;
set_tot();
}
Stock::~Stock()
{
}
void Stock::buy(long num,double price)
{
if(num<0)
{
std::cout<<"数量不能是负数。交易失败。\n";
}
else
{
shares+=num;
share_val=price;
set_tot();
}
}
void Stock::sell(long num,double price)
{
using std::cout;
if(num<0)
{
cout<<"数量不能是负数.\n";
}
else if(num>shares)
{
cout<<"卖出数量大于持有数量。交易失败。\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::endl;
using std::ios_base;
ios_base::fmtflags orig=cout.setf(ios_base::fixed,ios_base::floatfield);
std::streamsize prec =cout.precision(3);

cout<<"公司:"<<company<<" 股票数量:"<<shares<<endl
<<"每股价格:$"<<share_val;

cout.precision(2);
cout<<" 总价值:$"<<total_val<<endl;

cout.setf(orig,ios_base::floatfield);
cout.precision(prec);

}
const Stock& Stock::topval(const Stock& s) const
{
if(s.total_val>this->total_val)
{
return s;
}
else
return *this;
}
//类4main.cpp
#include"类4.h"
int main(){
Stock stocks[4]={
{"公司1",12,20.0},
{"公司2",200,2.0},
{"公司3",130,3.25},
{"公司4",60,6.5}
};
std::cout<<"展示对象数组:"<<std::endl;
for(int i=0;i<4;i++)
{
stocks[i].show();
}
const Stock *top=&stocks[0];
for(int i=1;i<4;i++)
{
top=&top->topval(stocks[i]);
}
std::cout<<"价值最高的股票是:"<<std::endl;
top->show();
}
PS D:\study\c++\path_to_c++> g++ -I .\include\ -o 类4 .\类4.cpp .\类4main.cpp
PS D:\study\c++\path_to_c++> .\类4.exe
展示对象数组:
公司:公司1 股票数量:12
每股价格:$20.000 总价值:$240.00
公司:公司2 股票数量:200
每股价格:$2.000 总价值:$400.00
公司:公司3 股票数量:130
每股价格:$3.250 总价值:$422.50
公司:公司4 股票数量:60
每股价格:$6.500 总价值:$390.00
价值最高的股票是:
公司:公司3 股票数量:130
每股价格:$3.250 总价值:$422.50

类作用域

在类中声明的名称(数据成员和成员函数的名称)的作用域是整个类。
也就是说,在类声明或成员函数定义中,可以使用未修饰的成员名称,在其他情况下,使用类成员名时必须使用,直接成员运算符(.)、简介成员运算符->、作用域解析符::

作用域为类的常量

我们有时候想要定义一个常量,但是这个常量是只能在类作用域中使用。

class Bakery
{
private:
const int Months=12;
double costs[Months];
...
};

上面这段代码是错误的,因为这里Months是自动变量,那就说明创建对象前,这个12并没有存储,那么创建对象时,costs就会创建失败。
解决这一问题,

class Bakery
{
private:
static const int Months=12;
double costs[Months];
...
};

Months改成静态存储即可。
或者你使用枚举,因为枚举相当于是宏定义#define ...

class Bakery
{
private:
enum {Months=12};
double costs[Months];
...
};

枚举类告诉编译器,把类作用域中的所有Months替换成12.

枚举类

传统的枚举,存在一些问题,比如,两个枚举定义中的枚举量会有冲突

enum egg{small,medium,large};
enum t_shirt{tiny,small,medium,large};

比如上面这段代码就会报错。
下面介绍一种新的枚举,作用域中的枚举

enum class egg{small,medium,large};
enum class t_shirt{tiny,small,medium,large};
egg a=egg::small;
t_shirt b=t_shirt::small;

为了枚举类的安全性,作用域中的枚举不会自动转化成整型
所以说,你如果想把枚举类型的变量视为整型,必须进行强制类型转化。

抽象数据类型

类常常被我们实现抽象数据类型,例如我们用类来搞一个栈(stack)

//类5.h
#ifndef STACK
#define STACK
typedef unsigned long Item;
class Stack
{
private:
static const int MAX=10;
Item items[MAX];
int top;
public:
Stack();
bool isempty() const;
bool isfull() const;
bool push(const Item& item);
bool pop(Item &item);
void show() const;
};
#endif
//类5.cpp
#include"类5.h"
#include<iostream>
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
{
std::cout<<"栈满,无法压栈\n";
return false;
}

}
bool Stack::pop(Item &item)
{
if(top>0)
{
item=items[--top];
return true;
}
else
{
std::cout<<"栈空,无法出栈\n";
return false;
}
}
void Stack::show() const
{ using namespace std;
cout<<endl<<"---top---"<<endl;
for(int i=top-1;i>=0;i--)
{
cout<<items[i]<<endl;
}
cout<<"---bottom---"<<endl<<endl;
}
//类5main.cpp
#include"类5.h"
#include<iostream>
int main(){
using namespace std;
Stack t;
char ch;
Item po;
cout<<"输入A:压栈 B:出栈\n";
t.show();
while (cin>>ch)
{
switch (ch)
{
case 'A':
cout<<"输入数字\n";
cin>>po;
t.push(po);
t.show();
break;
case 'B':
t.pop(po);
t.show();
break;

default:
goto aa;
break;
}
cout<<"输入A:压栈 B:出栈\n";
}
aa:return 0;
}
PS D:\study\c++\path_to_c++> g++ -I .\include\ -o 类5 .\类5.cpp .\类5main.cpp
PS D:\study\c++\path_to_c++> .\类5.exe
输入A:压栈 B:出栈

---top---
---bottom---

A
输入数字
1

---top---
1
---bottom---

输入A:压栈 B:出栈
A
输入数字
2

---top---
2
1
---bottom---

输入A:压栈 B:出栈
A
输入数字
3

---top---
3
2
1
---bottom---

输入A:压栈 B:出栈
B

---top---
2
1
---bottom---

输入A:压栈 B:出栈
B

---top---
1
---bottom---

输入A:压栈 B:出栈
B

---top---
---bottom---

输入A:压栈 B:出栈
B
栈空,无法出栈

---top---
---bottom---

输入A:压栈 B:出栈
Q

结语

目前,我们只是学会了类的通用原理,但是在实际使用类的时候,还有很多地方可以润色,接下来我们的重点可能会放在如何设计类?如何让类更美观、更方便?