文章目录
一、过程性编程和面向对象编程OOP
(1)采用过程性编程方法:
(2)OOP思想:
二、抽象和类
生活中充满复杂性,处理复杂性的方法之一是简化和抽象;
抽象是通往用户定义类型的捷径,在C++中,用户定义类型指的是实现抽象接口的类设计。
1.类型是什么?
(1)不能对指针执行与整数相同的运算。eg:不能将两个指针相乘,这种运算是没有意义的,so,将变量声明为int或float指针时,不仅仅是分配内存,还规定了可对变量执行的操作。
(2)指定基本类型完成了以下的三项工作:
2.C++中的类
(1)类是一种将抽象转换为用户类型的C++工具,它将数据表示和操纵数据的方法组合成一个整洁的包。
(2)接下来的3-5都是围绕着该eg进行的,所以必须得先说明这个eg
(3)定义类:类范围由两个部分组成:
- 类声明:以数据成员的方式描述数据部分,以成员函数(or叫方法) 的方式描述公有接口;
- 类方法定义:描述如何实现类成员函数。
注意:通常,C++程序员将接口(类定义) 放在头文件中,并将实现(类方法) 放在源代码的文件中。
(4)什么是接口??
(5)通常,C++程序员将接口(类定义) 放在头文件中,并将实现(类方法) 放在源代码的文件中。
eg如下:
// stock00.h -- Stock class interface
// version 00
#ifndef STOCK00_H_
#define STOCK00_H_
#include <string>
class Stock // class declaration
{
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();
}; // note semicolon at the end
#endif
说明:
(a)C++关键字class指出了这些代码定义了一个类设计;
Stock是这个新类的类型名,该声明能够声明Stock类型的变量——称之为对象或者实例。
对于描述函数接口而言,原型就足够了,将数据和方法组合成一个单元是类最吸引人的特性。
eg:
(b)访问控制:
(i)private,public,公有成员函数是干啥的??
(ii)封装是干啥的?
(iii)解释图
(iiii)OOP和C++之间的关系
(iiiii)数据隐藏的好处
(c)控制对成员的访问:公有还是私有?
(i)注意:使用私有成员函数来处理不属于公有接口的实现细节
(ii)对于private的关键字的省略问题——private是类对象的默认访问控制;
(iii)类和结构的区别
3.实现类成员函数:为类声明中的原型表示的成员函数提供代码
(1)类成员函数定义(就是类方法) 与常规函数的区别
eg:::叫所属于什么类
说明如下:
(2)类方法其他特点:可以访问类的私有成员
(3)eg
// stock00.cpp -- implementing the Stock class
// version 00
#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 0.\n";
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 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 -= num;
share_val = price;
set_tot();
}
}
void Stock::update(double price)
{
share_val = price;
set_tot();
}
void Stock::show()
{
std::cout << "Company: " << company
<< " Shares: " << shares << '\n'
<< " Share Price: $" << share_val
<< " Total Worth: $" << total_val << '\n';
}
说明:
(a)成员函数的说明,注意私有成员函数
(b)内联方法:针对于私有成员函数的
(c)如何将类方法应用于对象??
(d)定义两个类对象的说明,也可以说是定义的两个对象实例化的说明
4.使用类
(1)类对象的创建方法有很多,先可以了解下
(2)eg如下:
// usestok0.cpp -- the client program
// compile with stock.cpp
#include <iostream>
#include "stock00.h"
int main()
{
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.125);
fluffy_the_cat.show();
fluffy_the_cat.sell(300000,0.125);
fluffy_the_cat.show();
// std::cin.get();
return 0;
}
说明:
(1)
(2)用客户/服务器模型去理解类
5.修改实现——修改输出的小数点位数
(1)ostream类中包含一些可用于控制格式的成员函数(这两步都得有!!)
上面代码的意思是:给cout对象设置了一个标记,命令cout使用定点表示法。
上面代码的意思是:cout使用定点表示法时,显示三位小数
(2)
结果:
6.小节——重要
(1)指定类设计的第一步是提供类声明
(2)指定类设计的第二步是实现类成员函数
(a)私有成员函数用内联的方式实现!!
(b)公有成员函数呢?
(c)如何操作类对象呢?
三、类的构造函数和析构函数
(1)C++的目标之一是让使用类对象就像使用标准类型一样。
常规的初始化语法不适用于类型Stock,
说明:
(2)so,类对象的初始化方式可以是:
(3)类构造函数的提出
构造函数:专门用于构造新对象、将值赋给他们的数据成员。
构造函数的要求:
(a)C++为这些成员函数提供了名称和使用语法,而程序员需要提供方法定义;
(b)名称要与类名相同;
(c)构造函数的原型和函数有一个有趣的特征——虽然没有返回值,但没有被声明为void类型 (构造函数没有声明类型)
eg:
1.声明和定义构造函数
(1)创建Stock的构造函数的过程如下:
构造函数的声明如下:
构造函数可能的一种定义如下:
注意区别公有成员函数与构造函数的区别:
(2)不要将类的私有数据成员名作为构造函数的形参名
2.构造函数初始化对象:2种方式
(1)第一种方式:显示地调用构造函数
(2)第二种方式:隐式地调用构造函数
(3)构造函数不能通过对象来调用,而是用来创建构造对象的
(4)其它
3.使用默认构造函数:2种方法
(1)第一种方法:没有提供任何构造函数,C++将自动提供默认构造函数
(不懂的话,参考4.使用类那块内容)
(2)第二种方法:为类定义了构造函数后,就必须为它提供默认构造函数。
(不懂的话,参考5.改进Stock类:重要的eg)
定义默认构造函数的方式有两个:
(a)方法1:
一种是给已有构造函数的所有参数提供默认值
(b)方法2:
另一种是通过函数重载来定义另一个构造函数——一个没有参数的构造函数
(c)注意:
(d)隐式地or显式地使用默认构造函数、隐式地使用构造函数
4.析构函数
(1)析构函数的定义与作用
(2)析构函数的特点,以及与构造函数的区别
(3)什么时候调用析构函数??
5.改进Stock类:重要的eg
(1)程序说明;
(2)头文件
// stock10.h – Stock class declaration with constructors, destructor added
#ifndef STOCK1_H_
#define STOCK1_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(); // default constructor
Stock(const std::string & co, long n = 0, double pr = 0.0);
~Stock(); // noisy destructor
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
void show();
};
#endif
(3)实现文件
// stock1.cpp – Stock class implementation with constructors, destructor added
#include <iostream>
#include "stock10.h"
// constructors (verbose versions)
Stock::Stock() // default constructor
{
std::cout << "Default constructor called\n";
company = "no name";
shares = 0;
share_val = 0.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();
}
// class destructor
Stock::~Stock() // verbose class destructor
{
std::cout << "Bye, " << company << "!\n";
}
// other methods
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 -= num;
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;
// set format to #.###
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;
// set format to #.##
cout.precision(2);
cout << " Total Worth: $" << total_val << '\n';
// restore original format
cout.setf(orig, ios_base::floatfield);
cout.precision(prec);
}
(4)客户文件
// usestok1.cpp -- using the Stock class
// compile with stock10.cpp
#include <iostream>
#include "stock10.h"
int main()
{
{
using std::cout;
cout << "Using constructors to create new objects\n";
Stock stock1("NanoSmart", 12, 20.0); // syntax 1
stock1.show();
Stock stock2 = Stock ("Boffo Objects", 2, 2.0); // syntax 2
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 Foods", 10, 50.0); // temp object
cout << "Revised stock1:\n";
stock1.show();
cout << "Done\n";
}
// std::cin.get();
return 0;
}
总结:
(a)注意main()函数中,开头和结尾多了一个大括号
(b)隐式地声明构造函数和显式地声明构造函数
Stock stock1(“NanoSmart”,12,20.0)
等价于<=>
Stock stock1=Stock(“NanoSmart”,12,20.0)
(c)调用构造函数来创建临时对象,需要注意的是:临时变量啥时候析构(可能有,可能无)
注意:下面的红色箭头表示,在类对象赋值的过程后,对临时对象stock2使用析构的结果
有的编译器可能输出的情况如下:
(d)如果stock1对象已经存在
(e)局部变量都是放到栈中的
(过程:先是删除stock1 = Stock(“Nifty Foods”, 10, 50.0)的stock1的对象; 然后删除上面的Stock stock2 = Stock (“Boffo Objects”, 2, 2.0)的stock2对象,最后删除Stock stock1(“NanoSmart”, 12, 20.0)的stock1对象,输出的每个对象的内容要根据实际情况去看,很easy)
(f)给类对象赋值,采用初始化的方式比通过赋值类对象更好!
(4)C++11列表初始化方式可以用于类:用的少
(a)
(b)默认构造函数的eg
在main()函数加入如下的代码:
结果如下的红框:
(5)const成员函数,写成公有成员函数void stock::show() const的形式(因为没形参),保证函数不会修改调用对象
6.构造函数和析构函数小结
(1)构造函数是一种特殊的类成员函数,在创建类对象时被调用。
特点是:
- [a] 构造函数的名称和类名相同,但是通过函数重载(所以能匹配构造函数与默认构造函数的区别),可以创建多个同名的构造函数,条件是每个函数的特征标(参数列表)都不同。
- [b]构造函数没有声明类型。通常,构造函数用于初始化类对象的成员,初始化应与构造函数的参数列表匹配。
eg:
(2) 默认构造函数
(3)析构函数
(4)上面的这些说明最好结合能跑出结果的eg去理解,效果更佳