分支结构、函数和内存模型复习后没有登录做笔记,以后补上。
对象和类
从这里引入面向对象编程(OOP),下面是最重要的OOP特性:
- 抽象;
- 封装和数据隐藏
- 多态
- 继承
- 代码的可重用性
为实现上述功能,c++提供了类。
10.1 过程性编程和面向对象编程
采用过程性编程时,首先考虑的是遵循的步骤,然后考虑如何表示这些数据;
采用OOP方法时,首先从用户的角度考虑对象——描述对象所需的数据以及描述用户与数据交互所需的操作。完成对接口的描述后,需要确定如何实现接口和数据存储。最后,使用新的设计方案创建程序。
10.2 抽象和类
10.2.1 类型是什么
指定基本类型完成了三项工作:
- 决定数据对象需要的内存数量
- 决定如何解释内存中的位(float 和 long不同)
- 决定可使用数据对象执行的操作或方法
10.2.2 c++中的类
定义类由两部分组成:
- 类声明:以数据成员的方式描述数据部分,以成员函数(被称为方法)的方式描述公有接口
- 类方法定义:描述如何实现类成员函数
通常,c++将接口(类定义——类方法)放在头文件中,并将实现(类方法的代码)放在源代码文件中(实现细节和接口设计的分离)
#ifndef STOCK00_H_
#define STOK00_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();
};//类的声明!!!需要分号。
- 访问控制
使用类对象,可以直接访问公有部分,但只能通过公有成员函数(或友元函数)来访问对象的私有函数,因此,公有成员函数是程序和对象的私有成员之间的桥梁,提供了对象和程序之间的接口。防止程序直接访问数据被称为数据隐藏。
类设计尽可能将公有接口与实现细节分开。公有接口表示设计的抽象组件。将实现细节(数据和私有部分的函数实现细节)放在一起并将他们与抽象分开(公有函数,类方法)被称为封装。数据隐藏是一种封装,将实现细节隐藏在私有部分中也是一种封装,将函数定义与类声明放在不同的文件中也是封装。
OOP设计的原则是将实现细节从接口设计中分离出来,如果以后找到更好的,实现数据表示或成员函数细节的方法,可以对这些细节进行修改,而无需修改程序接口,这使程序维护起来更加方便。 - 控制对成员的访问:公有还是私有
数据隐藏是OOP的一种主要目标,数据通常放在私有部分,组成类接口的成员函数放在公有部分。也可以使用私有成员函数来处理不属于公有接口的实现细节。
不必再类声明中使用关键字private,因为这是类对象的默认访问控制。
10.2.3 实现类成员函数
和普通函数定义非常相似,有函数头和函数体,也可以有返回类型和参数,但是它们还有两个特殊的特征:
- 定义成员函数时,使用作用域解析运算符(::)来标识函数所属的类;//即表明不同的类的名称相同的函数也是不同的函数
- 类方法可以访问类的private组件
void Stock::update(double price)
void Buffon::update()
作用域解析运算符确定了方法定义对应的类的身份,类的其他成员不必使用作用域解析运算符,因为它们属于同一个类。试图使用非成员函数访问私有数据成员是不允许的,除了友元函数。
10.2.4 使用类
客户/服务器模型
客户使用类的程序,只能通过公有方式定义的接口使用服务器,这意味着客户唯一的责任是了解该接口。服务器的责任是确保服务器根据该接口可靠并准确地执行。服务器设计人员只能修改类设计的实现细节,不能修改接口。
10.2.6 小结
第一步:
类声明可以包括数据成员和函数成员。私有部分只能过成员函数方位。通常,数据成员被放在私有部分中,成员函数被放在公有部分中。
公有部分的内容构成了设计的抽象部分——公有接口。将数据封装到私有部分中可以保护数据的完整性,这被称为数据隐藏。
第二部:
实现类成员函数。单独提供类函数定义,使用作用域解析运算符来指出成员函数属于哪个类。
10.3 类的构造函数和析构函数
由于类的数据部分的访问是私有的,意味着程序不能直接访问数据成员,所有如果要对类的数据成员初始化也必须通过公有函数来实现,这种函数就是类的构造函数。
10.3.1 声明和定义构造函数
在类的定义中声明类的公有函数,在外部文件中定义公有函数。
构造函数在类的定义中声明,在外部文件中定义
Stock(const string &co, long n=0, double pr=0.0);
//
Stock::Stock(const string &co, long n, double pr)
{
...
}
程序在声明对象时,将自动调用构造函数。
10.3.2 使用构造函数
Stock food = Stock(“world Mason”, 50 , 2.5);
Stock garment(“Furry”, 35, 3.0);
10.3.3 默认构造函数
当且仅当没有定义构造函数时,编译器才会提供默认构造函数。
10.3.4 析构函数
用来释放new开辟的内存。
~Stock();
//定义
Stock::~Stock()
{
std::cout << "Bye, " << company << " !\n";
}
10.3.5 代码
//stock.h
#ifndef STOCK_H_
#define STOCK_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:
Stock();
Stock(const std::string &co, long n, double pr = 0.0);
~Stock();
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
void show();
};
#endif
//stock.cpp
#include <iostream>
#include "stock.h"
Stock::Stock()
{
std::cout << "Default constructor called\n";
company = "no name";
shares = 0;
share_val = 0;
total_val = 0.0;
}
Stock::Stock(const std::string &co, long n, double pr)
{
std::cout << "Constructor using " << co << " called\n";
company = co;
if (n < 0)
{
std::cout << "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()
{
std::cout << "Bye, " << company << " !\n";
}
void Stock::buy(long num, double price)
{
if (num < 0)
{
std::cout << "Number of shares purchased can't be negative; "
<< "Transaction is aborted.\n";
}
else
{
shares += num;
share_val = price;
set_tot();
}
}
void Stock::sell(long num, double price)
{
using std::cout;
if (num < 0)
{
cout << "Number of shares sold can't be negative; "
<< "Transaction is aborted.\n";
}
else if (num > shares)
{
cout << "You can't sell more than you have! "
<< "Transaction is aborted.\n";
}
else
{
shares -= shares;
share_val = price;
set_tot();
}
}
void Stock::update(double price)
{
share_val = price;
set_tot();
}
void Stock::show()
{
using std::cout;
using std::ios_base;
ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield);
std::streamsize prec = cout.precision(3);
cout << "Company: " << company << " Shares :" << shares << '\n';
cout << " Share Price: $" << share_val;
cout.precision(2);
cout << " Total Worth: $" << total_val << '\n';
cout.setf(orig, ios_base::floatfield);
cout.precision(prec);
}
//main.cpp
#include<iostream>
#include "stock.h"
int main()
{
{
using std::cout;
cout << "Constructors to create new objects\n";
Stock stock1("NanoSmart", 12, 20.0);
stock1.show();
Stock stock2 = Stock("NanoSmart", 12, 20.0);
stock2.show();
cout << "Assigning stock1 to stock2:\n";
stock2 = stock1;
cout << "Listing stock1 and stock2:\n";
stock1.show();
stock2.show();
cout << " Using a constructor to reset an object:\n";
stock1 = Stock("Nifty food", 10, 50.0);
cout << "Revised stock1:\n";
stock1.show();
cout << "Done\n";
}
system("pause");
return 0;
}
运行结果: