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类的最终版本
#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
|
#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; }
|
#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)
#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
|
#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; }
|
#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
|
结语
目前,我们只是学会了类的通用原理,但是在实际使用类的时候,还有很多地方可以润色,接下来我们的重点可能会放在如何设计类?如何让类更美观、更方便?