导图社区 对象和类
类和对象是两种以计算机为载体的计算机语言的合称。对象是对客观事物的抽象,类是对对象的抽象。类是一种抽象的数据类型。
编辑于2021-01-02 19:33:48第10章 对象和类
10.1 过程性编程和面向对象编程
面向对象编程(OOP)是一种特殊的、设计程序的概念性方法
OOP特性
· 抽象
· 封装和数据隐藏
· 多态
· 继承
· 代码的可重用性
10.2 抽象和类
10.2.1 类型是什么
基本类型完成的三项工作
· 决定数据对象需要的内存数量
· 决定如何解释内存中的位
· 决定可使用数据对象执行的操作或方法
10.2.2 C++中的类
1. 接口
接口是一个共享框架,供两个系统交互时使用
例如
计算机-打印机
用户-计算机
计算机-计算机
对于类来说
· 公众(public)时使用类的程序
· 交互系统由类对象组成
· 接口由编写类的人提供的方法组成
2. 类的特点
(1)控制访问
· private
· public
· protected
(2)控制对成员的访问:公有还是私有
不必在类声明中使用关键字private,因为这是类对象的默认访问控制
class World { float mass; // private by default char name[20]; // private by default public: void tellall(void); ... };
3. 类和结构
类
默认访问类型:private
使用类来实现类描述
结构
默认访问类型:public
表示纯粹的数据对象
10.2.3 实现类成员函数
类成员函数的特点
· 定义成员函数时,使用作用域解析运算符(::)来标识函数所属的类
· 类方法可以访问类的private组件
1. 成员函数说明
这个类将set_tot()声明为私有成员函数,即编写这个类的人可以使用它,但编写代码来使用这个类的人不能使用
2. 内联方法
内联函数的特殊规则
要求在每个使用它们的文件中都对其进行定义
简便方法
将内敛定义放在定义类的头文件中
声明方法
可以在类声明之外定义成员函数时加上inline限定符
class Stock { private: ... void set_tot(); // definition kept seperate public: ... }; inline void Stock::set_tot() // use inline definition { total_val = shares * share_val; }
3. 方法使用哪个对象
· 创建的每个新对象都有自己的存储空间,用于储存其内部变量和类成员
· 同一个类的所有对象共享同一组类方法
· 不同对象调用同一方法时,它们将执行同一个代码块,只是将这些代码用于不同的数据
10.2.4 使用类
· 声明类变量
· 使用new为类对象分配存储空间
· 可以将对象作为函数的参数和返回值
· 可以将一个对象赋给另一个
10.2.5 修改实现
1. 使用方法setf()可以避免显示科学技术法: std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
2. 修改部分程序
std::streamsize prec = std::cout.precision(3); // save preceding value ... std::cout.precision(prec); // reset to old value // store original flags std::ios_base::fmtflags orig = std::cout.setf(std::ios_base::fixed); ... // reset to stored values std::cout.setf(orig, std::ios_base::floatfield);
10.3 类的构造函数和析构函数
10.3.1 声明和定义构造函数
1. 构造函数的特点
· 名称和类名相同
· 没有返回值,也不是void类型
· 没有声明类型
2. Stock类的构造函数示例
类中声明
Stock(...);
类外部定义
Stock::Stock(...) { ... }
3. 成员名和函数名
· 函数的参数名不能与类的数据成员相同
· 解决办法
(1)在数据成员名中使用前缀m_
(2)在成员名中使用后缀_
10.3.2 使用构造函数
1. 显示调用
Stock food = Stock("World Cabbage", 250, 1.25);
2. 隐式调用
Stock garment("Furry Mason", 50, 2.5);
3. 使用new创建
Stock* pstock = new Stock("Electroshock Games", 18, 19.0);
10.3.3 默认构造函数
如果没有提供任何构造函数,C++将自动提供默认构造函数。它是默认构造函数的隐式版本,不做任何工作。默认构造函数可能如下: Stock::Stock() {}
10.3.4 析构函数
· 如果对象是通过new创建的,则它将驻留在栈内存或自由储存区中。当使用delete来释放内存时,析构函数将自动被调用
· 如果程序员没有提供析构函数,编译器将隐式地声明一个默认析构函数,并在发现导致对象被删除的代码后,才提供默认析构函数的定义
10.3.5 改进Stock类
1. 头文件
2. 实现文件
3. 客户文件
4. 程序说明
对于以下代码: Stock stock2 = Stock("Boffo Objects", 2, 2.0); C++标准允许编译器使用两种方式来执行这种语法。 (1)直接初始化创建stock2对象并赋予相应的值 (2)调用构造函数来创建一个临时对象,然后将该临时对象复制到stock2中并丢弃
stock1 = Stock("Nifty Foods", 10, 50.0); 此语句因为stock1已经存在,因此不是对其初始化,而是将新值赋给它。通过让构造函数创建一个新的、临时的对象,然后将其内容赋值给stock1
5. C++11列表初始化
在C++11中,列表初始化可以用于类。前提是提供的列表内容与某个构造函数的参数列表匹配: Stock hot_tip = {"Derivatives Plus Plus", 100, 45.0}; Stock temp {};
6. const成员函数
const Stock land = Stock("Kludgehorn Properties"); land.show(); 对于当前的C++来说,编译器将拒绝第二行。因为show()的代码无法确保调用对象不被修改。C++的解决方法是将const关键字放在函数的括号后面: void show() const; // promises not to change invoking object 同样,函数定义的开头应像这样: void Stock::show() const // promises not to change invoking
只要类方法不修改调用对象,就应将其声明为const
10.3.6 构造函数和析构函数小结
1. 设Bozo类的构造函数原型如下: Bozo(const char* fname, const char* lname);
· 初始化新对象
Bozo bozetta = Bozo("Bozetta", "Biggens"); Bozo fufu("Fufu", "O'Dweeb"); Bozo* pc = new Bozo("Popo", "Le Peu");
· 列表初始化
Bozo bozetta = Bozo{"Bozetta", "Biggens"}; // C++11 Bozo fufu{"Fufu", "O'Dweeb"}; // C++11 Bozo* pc = new Bozo{"Popo", "Le Peu"}; // C++11
2. 假设构造函数只有一个参数: Bozo(int age);
· 常规初始化对象
Bozo dribble = Bozo(44); Bozo roon(66);
· 初始化对象新方式
Bozo tubby = 32;// special form for one-argument constrctors
3. 对于未被初始化的对象,程序将使用默认构造函数创建
4. 如果构造函数使用了new,则必须提供使用delete的析构函数
10.7 抽象数据类型(ADT)
栈(*)
10.6 类作用域
10.6.1 作用域为类的常量
class Bakery { private: const int Months = 12; ... ... } 这是行不通的,因为声明类只是描述了对象的形式,并没有创建对象。因此在创建对象前,将没有用于储存至的空间。
实现的两种方式
1. 在类中声明一个枚举,作用域为整个类,因此枚 举可以为整型常量提供作用域为整个类的符号名称
class Bakery { private: enum {Months = 12}; ... ... }
注意,这种方式声明枚举不会创建类数据成员,也就是说,所有的对象都不包含枚举。Months只是一个符号名称,在作用域为整个类的代码中遇到它时,编译器将用12来替换它
在很多实现中,ios_base类在其共有部分完成了类似的工作,诸如ios_base::fixed等标识符就来自这里。其中,fixed是ios_base类中定义的典型枚举量
2. 使用关键字static
class Bakery { private: static const int Months = 12; ... ... }
这将创建一个名为Months的常量,该常量将与其他静态变量储存在一起,而不是储存在对象中。因此只有一个Months常量,被所有Bakery对象共享。
10.6.2 作用域内枚举(C++11)
1. 无法通过编译的枚举量
enum egg {Small, Medium, Large, Jumbo}; enum t_shirt {Small, Medium, Large, XLarge};
因为egg Small和t_shirt Small位于相同的作用域内
2. C++11的新枚举
enum class egg {Small, Medium, Large, Jumbo}; enum class t_shirt {Small, Medium, Large, XLarge};
也可以使用关键字struct代替class,其枚举量的定义域为类,需要使用枚举名来限定枚举量: egg choice = egg::Large; // the Large enumerator of the egg enum t_shirt Floyd = t_shirt::Large; // the Large enumerator of the t_shirt enum
有些情况下,常规枚举量将自动转换为整型。但作用域内枚举量不能隐式地转换为整型: enum egg_old {Small, Medium, Large, Jumbo}; // unscoped enum class t_shirt {Small, Medium, Large, XLarge};//scoped egg_old one = Medium; // unscoped t_shirt rolf = t_shirt::Large; // scoped int king = one; // implicit type conversion for unscoped int ring = rolf; // not allowed, no implicit type conversion if (king < Jumbo) // allowed std::cout << "Allowed\n"; if (king < t_shirt::Medium) // not allowed std::cout << "Not allowed\n";
默认情况下,C++11作用域内枚举的底层类型为int。另外,还提供一种语法,可用于做出不同选择: // underlying type for pizza is short enum class : short pizza {Small, Medium, Large, XLarge}; :short将底层类型指定为short,底层类型必须为整型。
10.5 对象数组
const int STKS = 10; Stock stocks[STKS] = { Stock("NanoSmart", 12.5, 20), Stock(), Stock("Monolithic Obelisks", 130, 3.25), }; 上述代码只初始化了数组的部分元素,因此余下的7个元素将使用默认构造函数进行初始化。
创建对象数组的方案:首先使用默认构造函数创建数组元素,然后花括号中的构造函数将创建临时对象,然后将临时对象的内容复制到相应的元素中
要创建对象数组,则这个类必须有默认构造函数
10.4 this指针(*)