文章目录
this指针(指向调用成员函数的对象,即类方法都给this指针存储了调用自己的对象的地址)
this指针被作为隐藏参数传递给了类方法,有了它,在方法内部就可以使用调用该方法的对象了
每个成员函数,包括构造函数和析构函数,都有一个this指针,指向调用这个方法的对象,好让方法知道是谁在调用自己,如果自己内部代码需要使用调用自己的对象,也有办法称呼这个对象(*this就是调用此方法的对象的别名啦)
const成员函数的this指针也是const的,即不能使用this指针修改对象的值
如果方法需要显式地引用调用它的对象,就要用this指针。this指针指向调用对象的地址,所以*this是调用对象的别名。
示例 比较两个对象的成员的值
如果要比较两个对象的成员的值的大小,那么需要用到2个对象,之前写的代码全部都只用了一个对象,但是我们并不需要像以前那样,把两个对象都作为参数传给方法,而是只需要传其中一个,另一个对象来调用这个方法,很棒吧,返回二者中某成员更大的一个对象
那这里说了要把对象作为参数和返回值传递,那么怎么传呢?之前说过了,要传递引用。即把对象的引用作为方法的参数和返回值,这样使用的仍是对象本身,不是副本,却无需笨拙复制。
再者,进一步考虑,前面说了,如果成员函数不修改对象的值,则尽量设计为const成员函数,这里只是比较,当然不会修改,所以理应设计为const的。并且之前学习按引用传参时强调了,一般都尽量把引用形参和返回值设计为const的。
综上两点,我们的比较方法的原型里需要三个const。
//原型
const Stock & 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;
}
如果没有this指针,那上面定义中最后一句代码,你抓破脑袋也不知该返回啥能表示你返回的是调用此函数的对象的引用
上面代码的total_val,以及之前代码的构造函数中直接写各个数据成员的名字,其实都是相当于把this->total_val中的this指针和间接成员运算符省略了而已
代码:
//stock.h
#ifndef STOCK_H_
#define STOCK_H_
#include <string>
class Stock //类声明 class declaration
//一个常见但不通用的约定:类名首字母大写
{
private://可不写,类对象的默认访问控制就是private
std::string company;
long shares;
double share_val;
double total_val;
//可以在成员函数中使用数据成员
//定义位于类声明中的函数自动成为内联函数,无需关键字inline
void set_tot(){total_val = shares * share_val;}
public:
Stock(const std::string & co, long n, double pr);//构造函数
Stock();//默认构造函数
~Stock();//析构函数destructor
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 // STOCK_H_
//Stock.cpp
#include <iostream>
#include "stock.h"
//构造函数没有返回值,参数的数目和类型根据类的私有成员确定
Stock::Stock(const std::string & co, long n, double pr)
{
//千万不要把构造函数的形参名称设置为和类数据成员一样
//否则会出现company = company;的错误语句
std::cout << "Constructor using " << co << " called.\n";
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();
}
//默认构造函数
Stock::Stock()
{
company = "no name";
shares = 0;
share_val = 0.0;
total_val = 0.0;
std::cout << "Default constructor called.\n";
}
//析构函数
Stock::~Stock()
{
std::cout << "Bye! " << company << "\n";
}
//类的成员函数的定义都必须用作用域解析运算符标识函数所属的类
void Stock::buy(long num, double price)
{
//增加持有的股份
if (num < 0)
{
std::cout << "Number of shares purchased can't be nagative. "
<< "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
<< " Shares: " << shares << '\n'
<< " 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);
}
const Stock & Stock::topval(const Stock & s) const
{
if (s.total_val > total_val)
return s;
else
return *this;
}
#include <iostream>
#include "stock.h"
int main()
{
//为了看清析构函数被调用,所以在return之前弄个块
{
using std::cout;
const Stock stock1 = {"NanoSmart", 12, 20.0};
stock1.show();
const Stock stock2 = {"World Cabbages", 100, 2.0};
stock2.show();
const Stock a1 = stock1.topval(stock2);
const Stock a2= stock2.topval(stock1);
a1.show();
a2.show();
}//自动调用析构函数,清理对象
return 0;
}
上面函数中的cerr是ostream类的另一个对象,把输出发送到标准错误流。
Constructor using NanoSmart called.
Company: NanoSmart Shares: 12
Share Price: $20.000 Total Worth: $240.00
Constructor using World Cabbages called.
Company: World Cabbages Shares: 100
Share Price: $2.000 Total Worth: $200.00
Company: NanoSmart Shares: 12
Share Price: $20.000 Total Worth: $240.00
Company: NanoSmart Shares: 12
Share Price: $20.000 Total Worth: $240.00
Bye! NanoSmart
Bye! NanoSmart
Bye! World Cabbages
Bye! NanoSmart
对象数组(每一个元素都是一个对象)
之前觉得结构数组很牛逼,对象数组和结构数组很相似,还更高级
对象数组的每一个元素都是一个对象,初始化对象数组时可以对每个对象使用不同的构造函数
可以只初始化前一部分的对象元素,剩余的则使用默认构造函数初始化
最常用的初始化对象数组的方案:
先用默认构造函数创建数组元素
再用花括号中的构造函数创建临时对象,再把临时对象们的内容复制到相应的对象中,这要求必须为类写默认构造函数
示例 求一个对象数组中某成员值最大的对象
//stock.h
#ifndef STOCK_H_
#define STOCK_H_
#include <string>
class Stock //类声明 class declaration
//一个常见但不通用的约定:类名首字母大写
{
private://可不写,类对象的默认访问控制就是private
std::string company;
long shares;
double share_val;
double total_val;
//可以在成员函数中使用数据成员
//定义位于类声明中的函数自动成为内联函数,无需关键字inline
void set_tot(){total_val = shares * share_val;}
public:
Stock(const std::string & co, long n, double pr);//构造函数
Stock();//默认构造函数
~Stock();//析构函数destructor
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 // STOCK_H_
//Stock.cpp
#include <iostream>
#include "stock.h"
//构造函数没有返回值,参数的数目和类型根据类的私有成员确定
Stock::Stock(const std::string & co, long n, double pr)
{
//千万不要把构造函数的形参名称设置为和类数据成员一样
//否则会出现company = company;的错误语句
std::cout << "Constructor using " << co << " called.\n";
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();
}
//默认构造函数
Stock::Stock()
{
company = "no name";
shares = 0;
share_val = 0.0;
total_val = 0.0;
std::cout << "Default constructor called.\n";
}
//析构函数
Stock::~Stock()
{
std::cout << "Bye! " << company << "\n";
}
//类的成员函数的定义都必须用作用域解析运算符标识函数所属的类
void Stock::buy(long num, double price)
{
//增加持有的股份
if (num < 0)
{
std::cout << "Number of shares purchased can't be nagative. "
<< "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
<< " Shares: " << shares << '\n'
<< " 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);
}
const Stock & Stock::topval(const Stock & s) const
{
if (s.total_val > total_val)
return s;
else
return *this;
}
#include <iostream>
#include "stock.h"
const int STKS = 4;
int main()
{
//为了看清析构函数被调用,所以在return之前弄个块
{
using std::cout;
Stock ar[STKS] = {
Stock("NanoSmart", 120, 2.5),
Stock(),
Stock("World Cabbage", 130, 1.25),
Stock("Sherry Games", 452, 1.35)
};
cout << "Stock holdings:\n";
int i;
for (i = 0; i < STKS; ++i)
ar[i].show();//非const对象也可以使用show函数
const Stock * top = &ar[0];
for (i = 0; i < STKS; ++i)
top = &top->topval(ar[i]);//->的优先级大于&
cout << "\nThe most valuable holding:\n";
top->show();
}//自动调用析构函数,清理对象
return 0;
}
Constructor using NanoSmart called.
Default constructor called.
Constructor using World Cabbage called.
Constructor using Sherry Games called.
Stock holdings:
Company: NanoSmart Shares: 120
Share Price: $2.500 Total Worth: $300.00
Company: no name Shares: 0
Share Price: $0.000 Total Worth: $0.00
Company: World Cabbage Shares: 130
Share Price: $1.250 Total Worth: $162.50
Company: Sherry Games Shares: 452
Share Price: $1.350 Total Worth: $610.20
The most valuable holding:
Company: Sherry Games Shares: 452
Share Price: $1.350 Total Worth: $610.20
Bye! Sherry Games
Bye! World Cabbage
Bye! no name
Bye! NanoSmart
this指针在C中的样子
如果把C++的有this指针的代码转换为C代码,那么就要显式把this作为参数传给函数(C++的每个类方法都隐式传了this指针的),并且在构造函数中都要用间接成员运算符才可以使用到调用当前函数的对象的数据成员。
总结 (OOP真香)
通过到目前对类的学习可以发现,主要的工作是类的设计上,即大部分时间精力都是在写类方法,只有少部分时间用于写主函数,且主函数只是为了测试类设计的是否正确合理。并且每次变更需求,增加需求,都只需要改类设计的实现代码,大部分东西都不需要改,仍然可以使用,真香