一、类的概述
类简介:
C++中通过定义一个类来定义自己的数据结构。(没错,它就是一个数据结构哈哈哈哈,别以为只有class声明才是类啊,我 struct也行的)
一个类定义了一个类型,以及与其关联的一组操作。
类的基本思想是数据抽象和封装。
数据抽象:一种依赖于接口和实现分离的编程技术
接口:用户所能执行的操作
类的实现:包括类的数据成员、负责接口实现的函数体以及定义类所需要的各种私有函数
封装:实现类的接口和实现的分离
类想要实现数据抽象和封装,需要首先定义一个抽象数据类型。
这就涉及到这样几个问题
(1)基础问题:定义格式是什么、定义在哪(一般定义在头文件)
(2)定义这个类是用来干什么的?
(3)怎样去实现这个类?
那么我来举个例子
我需要定义一个类,它用来记录书的交易情况
/*
定义一个实现书店交易的类
它要实现表示一本书的总销售额、售出册数、平均售价
那么要是实现这个类,得先定义其数据成员,然后是类所能提供得接口(实现类所需要的操作)
然后开始实现接口
*/
struct Sales_date
{
std::string isbn() const { return bookNo; }//返回ISBN编号
Sales_date& combine(const Sales_date); //将一个Sales_date对象加到另一个对象上去
double avg_price() const; //计算均价
std::string bookNo; //ISBN编号
unsigned units_sold = 0;//销售数量
double revenue = 0.0; //销售总额
};
//Sale_date 的非成员接口函数
Sales_date add(const Sales_date&, const Sales_date&); //执行两个Sales_date对象的加法
std::ostream &print(std::ostream&, const Sales_date&);//将Sales_date对象输出到ostream上
std::istream &read(std::ostream&, const Sales_date&); //从ostream上读入到Sales_date对象
(不用在意我定义的顺序,编译器会先编译类内的成员的声明,然后在处理函数体)
以上的接口函数中 isbn()在类内定义,combine和avg_price在类外定义。
类内定义类似于内联函数(inline)。
而关于该对象的非成员函数,我们选择定义在类外。
至此,我们就定义好了一个类。关于类的使用,我们始终要记得以下几点
1.类名是什么
2.它在哪里定义
3.它支持什么操作
二、类的接口函数
先引入先this这个东西的概念
(为节省墨水,我就不多举例子了,还是用上面类的定义。)
类内函数isbn()相当于隐式的内联函数
我们使用该函数时。一般是这个样子 test.isbn()。使用点操作符去访问test对象的isbn成员,然后调用它。
test对象执行了isbn()操作,实际上相当于隐式的返回test.bookNo。
But问题来了,为啥它返回的就是test的bookNo成员嘞?(终于要引出来了)
没错!!!就是因为this。
因为成员函数通过一个名为this的额外隐式参数来访问调用它的那个对象,当我们调用一个成员函数,用请求该函数的对象地址初始化this,例如,如果调用test.isbn()。
可以看作为 Sales_date::isbn(&test)。
敞亮了,传入了test的指针实参给形参this。那么this就是指向test的指针。
因此this就是指向调用函数的对象的指针。
回归问题,其实isbn是隐式的使用了this,那么isbn中的return bookNo,其实就是隐式的this—>bookNo
再敞亮点,咱们把this替换成实参,上式不就是test.bookNo嘛。
在类内是允许使用this指针的。尽管没必要,但是我们依旧可以改写isbn()达到相同的效果
std::string isbn() const { return this->bookNo};
关于上面
要解释的也就是那个const了(加它干啥呀,挠挠我掉发的头)
它是修饰this属性的,给this加个底层const
this是一个指向类类型非常量版本的常量指针。咋理解
我这样解释,我上面的this 就是 Sales_data *const类型。
它有一个顶层const限制,也就是说,它的内容(也可以说是它本身)不可被修改。
但是它没有底层const限制,也就是说它指向的对象的内容是非const型的(可以被修改)。
那么我要一个常量的对象使用isbn ,如果没有那个const修饰就会报错。。。
试图将一个const指针赋给一个非const的指针。这个const指的是底层const。
为了函数的灵活性,既能够被常量对象调用,也能被非常量对象调用。
来个全套const服务。
(但是别入坑啊,有时this作为左值的时候还是不需要底层const的。别搞错了应用场景)
接下来this的性质讲解交给这位哥
https://blog.csdn.net/jx232515/article/details/52759127
一条龙服务有没有,带你从坑跳坑。
接下来实现我们的接口函数。(一、中只是声明,没有实现函数体)
using namespace std;
ostream &print(std::ostream &os, const Sales_data &rhs)//输出书的编号 数量 总数 平均价格
{
os << rhs.isbn() << " " << rhs.units_sold << " "
<< rhs.revenue << " " << rhs.avg_price();
return os;
}
istream &read(std::istream& is, Sales_data& rhs)//输入书的编号 数量 价格
{
double price = 0;
is >> rhs.bookNo >> rhs.units_sold >> price;
rhs.revenue += price * rhs.units_sold;
return is;
}
double Sales_data::avg_price() const //求平均价格
{
if (units_sold)
return revenue / units_sold;
else
return 0;
}
Sales_data& Sales_data::combine(const Sales_data &rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上去
revenue += rhs.revenue;
return *this; //返回调用该函数的对象
}
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs;
sum.combine(rhs); //把rhs的数据成员加到sum当中
return sum;
}
int main(void)
{
Sales_data total; //保存当前求和的结果的变量
if (read(cin, total)) //读入第一笔交易到total
{
Sales_data trans;
while (read(cin, trans))//读入新的交易到trans
{
if (total.isbn() == trans.isbn())//如果新读入书编号和前面的相同
total.combine(trans); //执行相加操作
else
{
print(cout, total) << endl; //不相同
total = trans; //处理下一本书
}
}
print(cout, total) << endl; //输出最后的交易
}
else //没有任何交易
{
cerr << "No data?!" << endl; //通知用户
}
system("pause");
return 0;
}
至此类的一个框架搭好了,下篇我们继续向下探究类哈哈哈哈。