1. 过程性编程和面向对象编程
采用OOP(Object-Oriented Programming)方法时,首先从用户的角度考虑对象 -- 描述对象所需要的数据以及描述用户与数据交互所需的操作。完成对接口的描述后,需要确定如何实现接口和数据存储。最后,使用新的设计方案创建出程序。
2. 抽象和类:
【类的定义】面向过程的语言(如C语言)倾向于根据数据的外观(在内存中如何存储)来考虑数据类型。如char占用1个字节的内存,double 占用8个字节内存。类是一种将抽象转换为用户定义类型的C++工具,将数据表示和操作数据的方法合成一个整洁的包。
【如何定义类】
(1)类声明:以数据成员的方式描述数据部分,以成员函数(被称为方法)的方式描述公有接口
(2)类方法定义:描述如何实现成员函数
2.1. 类声明
以下为C++声明的Stock类型
1 // stock00.h -- stock class interface 2 // version 00 3 #ifndef STOCK00_H 4 #define STOCK00_H 5 #include <string> 6 7 class Stock // class declaration 8 { 9 private: 10 std::string company; 11 long shares; 12 double share_val; 13 double total_val; 14 void set_tot() {total_val = share * share_val;} 15 public: 16 void acquire(const std::string &co, long n, double pr); 17 void buy(long num, double price); 18 void sell(long num, double price); 19 void update(double price); 20 void show(); 21 } 22 #endif
【访问控制】
关键字private和public描述了对类的访问控制。使用类对象可以直接访问公有部分,但只能通过公有成员函数(或友元函数)
【数据隐藏】将数据放在类的私有部分中
【封装】将实现放在一起并将它们与抽象分开称之为封装。
(a)数据隐藏是一种封装
(b)将实现的细节隐藏在私有部分,像set_tot()也是一种封
(c)装将类函数定义和类声明放在不同的文件中也是一种封装
【控制对成员的访问:公有还是私有】
数据项通常是私有,组成类接口的成员函数公有;放在私有部分的成员函数,可以被其他公有接口调用,处理不属于公有接口的实现细节。
不必在类声明中使用关键字private,因为这是类对象的默认访问控制。如下面例子中的 mass和name, 默认为private
1 class World 2 { 3 float mass; // private by default 4 char name[20]; // private by default 5 public: 6 void tellall(void); 7 ... 8 }
【类与结构体的区别】
结构体默认的访问类型是public,而类为private。C++程序员通常使用类实现类描述,而把结构体限制为只表示纯粹的数据对象(常被成为普通老式数据,POD,Plain Old Data)
2.2. 类定义
还需要创建类描述的第二个部分:为那些由类声明中的原型表示的成员函数提供代码。成员函数定义与常规函数定义非常相似,它们有函数头和函数体,也可以有返回类型和参数。但是他们还有两个特性:
(1)定义成员函数时,使用作用域解析运算符(::)来标识函数所属的类。如Stock::acquire()表示acquire()方法属于Stock类
(2)类方法可以访问类的private组件。如Stock:show()方法中,直接使用company, shares等private变量。【注】如果试图使用非成员函数访问这些private变量,编译器禁止这样做(除非使用友元)
【实现类成员函数】
以下为Stock类的实现
1 // stock00.cpp -- implementing the Stock class 2 // version 00 3 #include <iostream> 4 #include "stock00.h" 5 void Stock::acquire(const std::string &co, long n, double pr) 6 { 7 company = co; 8 if (n<0){ 9 std::cout << "Number of shares can't be negative;" 10 << company << " shares set to 0.\n"; 11 shares = 0; 12 } 13 else 14 shares = n; 15 share_val = pr; 16 set_tot(); 17 } 18 19 void Stock::buy(long num, double price) 20 { 21 if (num<0) { 22 std::cout << "Number of shares purchase can't be negative." 23 << "Transaction is aborted.\n"; 24 } 25 else { 26 shares += num; 27 share_val = price; 28 set_tot(); 29 } 30 } 31 32 void Stock::sell(long num, double price) 33 { 34 using std::cout; 35 if (num < 0) { 36 cout << "Number of shares sold can't be negative." 37 << "Transaction is aborted.\n"; 38 } 39 else if (num > shares) { 40 cout << "You can't sell more than you have! " 41 << "Transaction is aborted.\n"; 42 } 43 else { 44 shares -= num; 45 share_val = price; 46 set_tot(); 47 } 48 } 49 50 void Stock::update(double price) 51 { 52 share_val = price; 53 set_tot(); 54 } 55 56 void Stock::show() 57 { 58 std::cout << "Company: " << company 59 << "Shares: " << shares << '\n' 60 << "Share Price: $" << share_val 61 << "Total Worth: $" << total_val << '\n'; 62 }
acquire(), update(), buy(), sell() 四个成员函数涉及到对total_val的修改,为了方便,将其提取出来在set_tot()函数中实现。由于set_tot()函数只是实现代码的一种方式,而不是公有接口的组成部分,因此这个类将其声明为私有成员函数(即编写这个类的人可以使用它,但编写代码使用这个类的人不能使用它)。如果代码很长,则这种方法可以省去许多重复代码。
【内联方法】
(1)定义于类声明的函数,自动成为内联函数。如stock00.h Line14中的 set_tot() 函数
(2)在类声明外定义函数,只需在类实现部分中定义函数时使用inline限定符
1 class Stock 2 { 3 private: 4 ... 5 void set_tot(); // definition kept separate 6 public: 7 ... 8 }; 9 10 inline void Stock::set_tot() // use inline in definition 11 { 12 total_val = shares * share_val; 13 }
2.3. 使用类
C++程序员将接口(类定义)放在头文件中,将类的实现(类方法的代码)放在源文件中。利用下面的语句,可以创建Stock对象
1 // userstock00.cpp -- the client program 2 // compile with stock00.cpp 3 #include <iostream> 4 #include "stock00.h" 5 int main() 6 { 7 Stock fluffy_the_cat; 8 fluffy_the_cat.acquire("NanoSmart", 20, 12.50); 9 fluffy_the_cat.show(); 10 fluffy_the_cat.buy(15, 18.125); 11 fluffy_the_cat.show(); 12 fluffy_the_cat.sell(400, 20.00); 13 fluffy_the_cat.show(); 14 return 0; 15 }