第10章 对象和类 -1

本章内容:

 过程性编程和面向对象编程

 类概念

 如何定义和实现类

 公有类访问和私有类访问

 类的数据成员

 类方法(类函数成员)

 创建和使用类对象

 类的构造函数和析构函数

 const成员函数

 this指针

 创建对象数组

 类作用域

 抽象数据类型

面向对象编程(OOP)特性:
 抽象
 封装和数据隐藏
 多态
 继承
 代码的可重用性
为实现这些特性并将其组合在一起,C++提供了类。

10.1 过程性编程和面向对象编程

10.2.2 C++中的类

是一种将抽象转换为用户定义类型的C++工具,它将数据表示和操纵数据的方法组成一个整洁的包。

比如股票,stock类,可存储以下信息:
 公司名称
 所持股票数量
 每股价格
 股票总值

可执行操作为:
 获得股票
 增持
 卖出股票
 更新股票价格
 显示关于所持股票的信息

一般来说,类规范由两个部分组成:
 类声明:以数据成员的方式描述数据部分,以成员函数(方法)的方式描述公有接口
 类方法定义:描述如何实现类成员函数
简而言之,类声明提供类的蓝图,方法定义提供细节。
本书遵循一种常见但不通用的约定——类名首字母大写。
程序清单10.1 stock00.h

#ifndef STOCK00_H_
#define STOCK00_H_
#include <string>
class Stock
{
 private:
std::string company;
long shares;
double share_val;
double total_val;
void set_tot(){total_val = shares * shre_val;}
public:
   void acquire(const std::string &co, long n, double pr);
   void buy(long num, double price);
   void sell(long num, double price);
   void update(double price);
   void show();
}
#endif

此处的class和typename非同义词,不可替换。可通过“ Stock sally; ”、“ Stock solly; ”创建两个分别名为sally和solly的Stock对象,分别表示sally或solly持有的某公司股票。

首尾的代码段:

#ifndef STOCK00_H_
#define STOCK00_H_
...
#endif

C或C++中头文件可以互相包含,为避免重复定义,可使用#ifndef来规避;注意其需要和#endif配套使用。
1、 访问控制
在这里插入图片描述
使用类对象的程序都可以直接访问公有部分,但只能通过公有成员函数(或友元函数)
来访问对象的私有成员。例如要修改Stock类的shares成员,只能通过Stock的成员函数。因此,公有成员函数是程序和对象的私有成员之间的桥梁,提供了对象和程序之间的接口。防止程序直接访问数据被称为数据隐藏(一种封装)。

类设计尽可能将公有接口和实现细节分开。公有接口表示设计的抽象组件。将实现细节放在一起并将它们与抽象分开称为封装。数据隐藏(将数据放在类的私有部分中)是一种封装;将实现的细节隐藏在私有部分中(Stock类中的set_tot())也是一种封装;将类函数定义和类声明放在不同的文件中也是一种封装

数据隐藏可以防止直接访问数据,还可让开发者(类用户)无需了解数据数据是如何被表示的。将实现细节从接口设计中分离出来,后续可针对细节修改,而无需修改程序接口,使得维护更容易。

2、 控制对成员的访问:公有还是私有
类成员、数据成员或成员函数都可以在类的公有部分或私有部分进行声明。但由于隐藏数据时OOP主要的目标之一,因此数据项通常放在私有部分,组成类接口的成员函数放在公有部分。

可在类声明中省略关键字private(类对象的默认访问控制)。此系列笔记为强调数据隐藏的概念将显示地使用private。

类和结构
区别:结构的默认访问类型public,类为private。通常用类来描述具体的实现,而用结构来纯粹地表示数据。

10.2.3 实现类成员函数

类成员函数类似常规函数定义,但有两个特殊特征:
 需使用作用域解析运算符::来标识所属的类;
 类方法可以访问类的private组件

首先,列入update()成员函数的函数头:

 void Stock::update(double prive)

上述代码表示定义的update()函数时Stock类的成员。且还可将另一个类的成员函数也命名为update(),例如 void Buffoon::update()
标识符update()具有类作用域:即Stock类的其他成员函数不必使用作用域解析运算符,就可以使用update()方法,因为它们属于同一个类。
在这里插入图片描述
在这里插入图片描述

  1. 成员函数说明
    acquire()函数管理对某个公司股票的首次购买,buy()sell()管理增加或减少持有的股票。
    内联方法

定义位于类声明中的函数都将自动成为内联函数,比如Stock::set_tot()。类声明通常将短小的成员函数作为内联函数。也可在类声明之外定义成员函数为内联函数,只需在类实现部分中定义函数是使用关键字inline限定符即可:
在这里插入图片描述

10.2.4 使用类

Stock类的测试实现代码:
在这里插入图片描述
要使用新类型,最关键是要了解成员函数的功能,而不必考虑其实现细节

客户/服务器模型
OOP程序员常依照客户/服务器模型来讨论程序设计:客户是使用类的程序,类声明(包括类方法)构成了服务器,它是程序可以使用的资源。客户只能通过公有方式定义的接口使用服务器。服务器(服务器设计人员)的责任是确保服务器根据该接口可靠并准确地执行。服务器设计人员只能修改类设计的实现细节,而不能修改接口。

10.2.6 小结

指定类设计的第一步是提供类声明。类声明类似结构声明,可以包括数据成员和函数成员。
声明包含私有部分(常放数据成员),在其中声明的成员只能通过成员函数进行访问;声明也包含公有部分(常放成员函数 ),在其中声明的成员可被使用类对象的程序直接访问。

指定类设计的第二步是提供函数定义(除非函数很小)。需要使用作用域解析运算符来指出函数属于哪个类。

10.3 类的构造函数和析构函数

上个章节提供的Stock类代码并不能像初始化int和结构那样来初始化Stock对象,即常规的初始化语法不适用于类型Stock:

int year = 2022struct thing{  
char *pn;
int m;  
} 
thing amabob = {“wodget”, -23};
Stock hot = {“Sukie’s Autos, Inc., 200, 50.23};    //invalid,compile error

不能初始化Stock对象的原因是因为数据部分的访问状态是私有的,也就是说程序不能直接访问数据成员。程序只能通过成员函数来访问数据成员,因此需要设计合适的成员函数才能初始化对象。(若是数据成员成为公有,也可按常规方法初始化类对象,但会违背数据隐藏的特性)

C++提供一个特殊的成员函数——类构造函数,专门用于构造新对象、将值赋给它们的数据成员。在用类创建一个对象的时候会自动调用对应的构造函数为数据成员进行赋值。

10.3.1 声明和定义构造函数

构造函数和类同名,没有返回值(void也不写)

Stock::Stock(const string &co, long n, double pr)
{
   company = co;
   shares = … 
     …
}    //初始化赋值

注意此种写法是错误的:

Stock::Stock(const string &company, long shares, double share_val)
{}

构造函数的参数表示的不是类成员,而是赋给类成员的值,因此参数名不能与类成员相同,否则会出现 shares = shares。常见做法是在数据成员命中使用前缀m_或后缀 _

class Stock
{
private:
string m_company;     //string company_;
long m_shares;        //long shares_;

此时就可在公有接口或参数名中包含company和shares。

10.3.2 使用构造函数

C++提供两种使用构造函数初始化对象的方式:显示隐式

//显示:
Stock food = Stock(“Furry Mason”, 50, 2.5);	

//隐式:
Stock food(“Furry Mason”, 50, 2.5);

每次创建类对象(包括使用new动态分配内存)时,C++都使用类构造函数
将构造函数与new一起使用时:

Stock *food = new Stock(“Furry Mason”, 50, 2.5);

无法使用对象来调用构造函数,因为在构造函数构造出对象之前,对象是不存在的。因此构造函数被用来创建对象而不能通过对象来调用。

10.3.3 默认构造函数

默认构造函数是在未提供显示初始值时用来创建对象的构造函数,即如下这种声明的构造函数:

Stock fluffy;

上述代码可用在于如果没有提供构造函数,C++将自动提供默认构造函数,它为默认构造函数的隐式版本。对于Stock类,默认构造函数可能如下:

Stock::Stock(){}

故将创建fluffy对象,但不初始化其成员,类似“ int x ”,初始化x但不给其赋值。
当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数。为类定义了构造函数后,则必须提供默认构造函数;如果提供了非默认构造参数(如Stock(const string &co, long n, double pr))但没有提供默认构造参数,声明“ Stock stock1 ”将出错,因为此处自定义了构造参数与之不匹配。

默认构造函数和构造函数的区别:构造函数声明中一定有不确定的值,而默认构造函数声明中的值都是确定的。

定义默认构造函数的两种方式:
 给已有构造函数的所有参数提供默认值:

Stock(const string & co = “Error”, int n = 0, double pr = 0.0);

 通过重载来定义另一个构造函数——一个没有参数的构造函数:

//声明
Stock();

//定义
Stock::Stock()
{
   company = “no name”;
   share = 0;
   share_val = 0.0;
   total_val = 0.0;
}

C++只允许有一个默认构造参数,因此上述两种方式不能同时采用

注意:在设计类时,通常应提供对所有类成员做隐式初始化的默认构造函数。
创建了默认构造函数后,便可声明对象而不对它们进行显示初始化。

Stock first;     //隐式调用默认构造函数
Stock first = Stock();     //显示调用默认构造函数  
Stock *prelief = new Stock;       //隐式

隐式调用默认构造参数时不要使用圆括号。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我宿孤栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值