对象和类:面向对象编程(OPP)是一种特殊的,设计程序的概念性方法,OPP特性:
1、抽象 2、封装和数据隐藏 3、多态 4、继承 5、代码的可重用性
为了将上述特性组合一起,C++最重要的改进是提供了类。
过程性编程方法:首先考虑要遵循的步骤,其次考虑如何表示这些数据(注意:并不需要程序一直运行,用户可能希望能够将数据存储在一个文件汇总,然后从文件中读取数据)
对象性编程方法:首先从用户的角度考虑对象--描述对象所需要的数据以及描述用户与数据交互所需的操作。完成对接口的描述后,需要确定如何实现接口和数据存储,最后使用新的设计方案创建出程序。
1.1 类型是什么?
在C语言中,倾向于根据数据的外观(在内存中如何存储)来考虑数据类型,例如,char占用1个字节的内存,double通常占用8个字节的内存,即可以根据要对它执行的操作来定义数据类型。例如,int类型可以使用所有的算术运算,可以对整数执行加减乘除运算,还可以使用求模操作符(%)
指针需要的内存数量很可能与int相同,也可能在内部被表示为整数,但不能对指针执行与整数的相同运算。因此,将变量声明为int或float指针时,不仅是分配内存,还规定可对变量执行的操作。指定基本类型完成3项工作:
- 决定数据对象需要的内存数量
- 决定如何解释内存中的位(long和float在内存中占用的位数相同,但将其转换为数值的方法不同)
- 决定可使用数据对象执行的操作或方法
1.2 C++中的类
类是一种将抽象转换为用户定义类型的C++工具,它将数据表示和操纵数据的方法组成一个简洁的包。
类的定义由两个部分组成:
- 类声明:以数据成员的方式描述数据部分,以成员函数(方法)的方式描述共有接口,即类声明提供了类的蓝图
- 类方法定义:描述如何实现类成员函数,即方法定义提供了细节
接口的定义:接口是一个共享框架,供两个系统交互使用。对于类,俗称公共接口,公众(public)是使用类的程序,交互系统由类对象组成,而接口由编写类的人提供方法组成,接口让程序员能够编写与类对象交互的代码,从而让程序能够使用类对象。例如:要计算string对象中包含多少字符,无需打开对象,而只需要使用string类提供的size( )方法。
1.3 程序实例实现
1.3.1 类的声明
Stock类的类声明--一般将类名首字符大写
private: //写在private出现的标识,无论函数或数据某变量、类型等只能通过public的共用成员才能访问,即对数据进行隐藏(防止其他程序的干涉),通过public成员间接访问。
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 * share_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
类设计尽可能将公有接口和实现细节分开。
公有接口表示设计和抽象的组件,将实现细节放在一起并将其抽象分开被称为封装。数据隐藏(将数据放在类的私有部分中)是一种封装,将实现的细节隐藏在私有部分中也是封装。数据隐藏不仅可以放置直接访问数据,还让开发者无需了解数据是如何被表示的。
隐藏数据是OOP主要部分之一,因此数据项通常放在私有部分,组成类接口的成员函数放在公有部分。
类和结构描述较为相似,建议结构体用于定义数据类型;类用于定义数据类型及函数
1.3.2 实现类成员函数
为类声明中的原型表示的成员函数提供代码,特征如下:
- 定义成员函数时,使用作用域解析操作符(::)来标识函数所属的类
- 类方法可以访问类的private组件
stock00.cpp文件:
#include <iostream>
#include "stock00.h"
//编写函数具体实现
void Stock::acquire(const std::string &co, long n, double pr)
{
company = co;//在公共定义的函数里访问私有的成员变量
if(n < 0)
{ //不能购买负的股票
std::cout << "Number of shares can't be negative; "<< company << "shares set to be 0."<<std::endl;
shares = 0;
}
else
shares = n;
share_val = pr;
set_tot();
}
void Stock::buy( long num , double price )
{
if(num < 0)
{ //不能购买负的股票
std::cout << "Number of shares can't be negative,tuansaction is aborted "<<std::endl;
}
else
{
shares += num;
share_val = price;
set_tot();
}
}
void Stock::sell(long num, double price)
{
using std::cout;
if(num < 0)
{ //不能卖负的股票
std::cout << "Number of shares can't be negative,tuansaction is aborted "<<std::endl;
}
else if (num >shares) //卖出股票的数目不能超出当前手中的数目
{
cout << "You can't sell more than you have! Transanction is aborted"<<std::endl;
}
else
{
shares -= num;
share_val = price; //更新当前股票价格
set_tot();//计算当前资产
}
}
void Stock::update(double price) //更新当前股票的价格
{
share_val = price;
set_tot();//更新当前的总资产
}
void Stock::show()
{
std::cout << "Company: " << company << std::endl;
std::cout << "Share: " << shares << std::endl;
std::cout << "Share price "<< share_val << std::endl;
std::cout << "Total worth: "<< total_val << std::endl;
}
内联函数(都放在头文件里):
定义位于类声明中的函数都将自动成为内联函数,因此Stock::set_tot()是一个内联函数,类声明常将短小简单的成员函数作为内联函数void set_total() {total_val = share * share_val};符合。
内联函数特殊规则要求在每个使用它们的文件中都要对其进行定义,最为简便方法:将内联定义放在定义类的头文件中。
1.3.3 使用类
usestock00.cpp文件:
#include <iostream>
#include "stock00.h"
int main (void)
{
Stock fluffy_the_cat;
fluffy_the_cat.acquire("NanoSmart",20 , 12.50);
fluffy_the_cat.show();
fluffy_the_cat.buy(15,18.125);
fluffy_the_cat.show();
fluffy_the_cat.sell(400,20.00);
fluffy_the_cat.show();
fluffy_the_cat.buy(300000,40.15);
fluffy_the_cat.show();
fluffy_the_cat.sell(300000,0.125);
fluffy_the_cat.show();
return 0;
}
1.3.4 程序运行结果 :
1.4 小结
1.4.1 指定类设计第一步:提供结构类声明
类声明类似结构声明,可以包括数据成员和函数成员。声明有私有部分,在其中声明的成员智能通过成员函数进行访问;声明还具有公有部分,在其声明的成员可被使用类对象的程序直接访问:
典型类声明格式
Class className
{
Private:
Data member declarations
Public:
Member function prototypes
};
共有部分的内容构成了设计的抽象部分--共有接口。将数据封装到私有部分中可以保护数据的完整性,称之为数据隐藏,
1.4.2 指定类设计第二步:实现类成员函数
在类声明中提供完整的函数定义,而不是函数原型,但通常的做法是单独提供函数定义,此情况下,需要使用作用域解析操作符指明成员函数属于哪个类。