定义抽象类型数据

文章介绍了如何在C++中设计和使用抽象类型数据,以`Sales_data`类为例,详细阐述了类的接口设计、成员函数(包括const成员函数和this的使用)、非成员函数的定义、构造函数的使用,以及默认构造函数的概念。文章强调了数据抽象和封装在类设计中的重要性。
摘要由CSDN通过智能技术生成


前言

类的基础思想是数据结构和封装。数据抽象是一种依赖于接口和实现分离的编程技术。类的接口包括用户所能实执行的操作;类的实现则包括类的成员结构、负责接口实现的函数体以及定义类所需的各种私有函数。封装实现了类的接口和实现的分离。封装后隐藏了它的实现细节。
类想要实现数据抽象和封装,需要首先定义一个抽象数据类型。在抽象数据类型中,又累的设计者负责考虑类的实现过程;使用该类的程序员则只需要抽象地思考类型做了什么,而无需了解类型的工作细节。


定义抽象类型数据

1.1. 设计Sales_data类

基于设计一个图书管理系统的类,所以Sales-data的接口应该包括一下操作:

  • 一个isbn成员函数,用于返回对象的ISBN码
  • 一个combine成员函数,用于将一个Sales-data对象加到另一个对象上
  • 一个名为add的函数,执行两个Sales-data的相加
  • 一个read函数,将数据从istream读入到Sales-data对象中
  • 一个print函数,将Sales-data对象的值输出到ostream

使用Sales_data类

#include<iostream>
#include<string.h>
#include"Sales_data.h"
using namespace std;
int main()
{
	Sales_data total;
	if(read(cin,total))
	{
		Sales_data trans;
		while(read(cin,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;
}

1.2.定义Sales_data类

struct Sales_data
{
//关于Sales_data对象的操作
	string isbn() const {return bookNo;}
	Sales_data& combine(const Sales_data&);
	double avg_price() const;//返回书籍的平均价格
//数据成员
	string bookNo;
	unsigned units_sold=0;
	double revenue=0.0;
};
//Sales_data的非成员接口函数
Sales_data add(const Sales_data&, const Sales_data&);
ostream &print(ostream&,const Sales_data&);
istream &read(istream&,Sales_data&;

定义和声明成员函数的方式与普通函数差不多。成员函数的声明必须在类的内部,他的定义则既可以在类的内部,也可以在类的外部。作为接口组成部分的非成员函数,他们的定义和声明都在类的外部。

1.2.1引入this

在total.isbn()的调用中,实质上是编译器将total的地址传递给了isbn的隐式形参this上
伪代码如下:

total.isbn()
Sales_data::isbn(&total)

这两种意义相同。
我们还可以把isbn定义成如下形式:

string isbn() const {return this->bookNo;}

因为this是一个指针常量,我们不允许改变this中保存的地址。

1.2.2.引入const成员函数

默认情况下,this的类型是指向类类型非常量版本的常量指针,但是this仍需要遵循初始化规则,这就意味着我们不能把this绑定到一个常量对象上。使得我们不能调用普通的成员函数。
则我们将this的声明成const Sales_data *const。如此既不会改变this所指对象,等有助于提高函数的灵活性。

1.2.3. 在类的外部定义成员函数

像其他函数一样,当我们在类的外部定义成员函数时,成员函数的定义必须和他的声明匹配。
如下:

double Sales_data::avg_price() const
{
	if(units_sold)
	return revenue/units_sold;
	else
	return 0;
}

其中使用作用域运算符Sales_data::avg_price,所以当avg_price使用revenue和units_sold时,实际上隐式的使用了Sales_data的成员。

1.2.4定义一个返回this对象的函数

函数combine的设计初衷类似于复合赋值运算符+=,调用该函数的对象代表得是赋值运算符左侧的运算对象,右侧运算对象则通过显式的实参被传入函数:

Sales_data& Sales_data::combine(const Sales_data &rhs)
{
	units_sold+=rhs.units_sold;
	revenue+=rhs.revenue;
	return *this;
}

当我们的处理交易程序调用如下的函数时,

total.combine(trans);

此时total的地址被绑定在隐式的this参数上,而rhs绑定到了trans上,因此函数相当于:

units_sold+=rhs.units_sold;

最后返回total的引用。


1.3.定义类相关的非成员函数

设计一个类常常需要定义一些辅助函数,比如add、read和print等,虽然这些函数属于类的接口的组成部分,但他们实际上并不属于类。

1.3.1定义read和print函数

istream &read(istream &is,Sales_data &item)
{
	double price=0;
	is>>item.bookNo>>item.units_sold>>price;
	item.revenue=price*item.units_sold;
	return is;
}
ostream &price(ostream &os,const Sales_data &item)
{
	os<<item.isbn()<<" "<<item units_sold >>" "
		<<item.revenue<<" "<<item.avg_price();
	return os;
}

两点值得注意:
1.read和print分别接受一个各自IO类型的引用作为参数,因为IO类属于不能拷贝类型,因此我们只能通过引用传递他们。因为都读入和写入的操作会改变流的内容,所以函数接收的都是普通引用。
2.print函数不负责换行,执行输出任务的函数应该尽量减少对格式的控制。

1.3.2定义add函数

Sales_data add(const Sales_data &lhs,const Sales_data &rhs)
{
	Sales_data sum=lhs;
	sum.combine(rhs);
	return sum;
}

1.4.构造函数

每个类都分别定义了他的对象被初始化的方式,类通过一个或几个特殊的成员函数来控制其对象初始化的过程,这些函数叫做构造函数。构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。

1.4.1合成的默认的构造函数

类通过一个特殊的构造函数来控制默认初始化过程,这个函数叫做默认构造函数。默认构造函数无需任何实参。如果我们的类没有显式的定义构造函数,那么编译器就会为我们隐式的定义一个默认构造函数。,编译器创建的构造函数又称为合成的默认构造函数,有一下规则:
1.如果存在类内的初始值,用它来初始化成员。
2.否则,默认初始化该成员。

1.4.2 定义Sales_data的构造函数

对于我们的Sales_data类来说,我们将使用下面的参数定义4个不同的构造函数;

  • 一个istream& ,从中读取一条交易信息。
  • 一个const string& ,表示ISBN编号;一个unsigned,表示售出的图书数量;以及一个double,表示图书的售出价格。
  • 一个const string&,表示ISBN编号;编译器将赋予其他成员默认值。
  • 一个空参数列表。
struct Sales_data {
	Sales_data()=default;
	Sales_data(const string &s):bookNo(s){}
	Sales_data(const string &s,unsigned n.double p):
			   bookNo(s),units_sold(n),revenue(p*n){}
	Sales_data(istream &);
	string isbn() const {return bookNo;}
	Sales_data& combine(const Sales_data&);
	double avg_price() const;
	string bookNo;
	unsigned units_sold=0;
	double revenue=0.0;
};

1.4.3=default的含义

如果我们既需要其他形式的构造函数,也需要默认的构造函数,那在c++11新标准中,如果我们需要这种默认行为,那么可以通过在参数列表后面写上=default来要求编译器生成构造函数。其中,=default既可以和声明一起出现在类的内部,也可以作为定义出现在类的外部。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值