C++ Primer Plus 第10章 对象和类

第10章 对象和类
思维导图

在这里插入图片描述

1.OOP特性和相关概念
1.1 接口

接口是一个共享框架,由编写类的人提供的方法组成,使得程序能够使用类对象。

在这里插入图片描述

1.2 抽象

类的公共接口就是对类的抽象

1.3 用户定义类型

构成基本类型的要素:

决定数据对象需要的内存数量;存储

决定如何解释内存中的位;(long和float在内存中占用的位数相同,但是将其转化为数值的方法不同)

决定操作数据对象的方法;

C++中,用户定义类型就是指实现了抽象接口的类设计。

1.4 类规范

类声明:以数据成员的方式描述数据部分,以函数成员的方式描述公有接口。

类方法定义:描述如何实现类成员函数。

类声明提供了类的蓝图,方法定义提供了细节。

1.5 访问控制

关键字private、public描述了对于类成员的访问控制,类对象的默认访问控制是private。

使用类对象的程序都可以直接访问类的公有部分;使用类对象的程序必须通过类的公有部分间接访问类的私有成员。

类的公有部分包括公有成员函数友元函数;类的公有部分可以直接访问类的私有成员。

类的公有部分构成了公共接口,是使用类的程序和私有成员之间的桥梁。

类的私有成员无法被程序直接访问,使得数据被隐藏。

在这里插入图片描述

1.6 数据隐藏

防止程序直接访问数据称为数据隐藏

数据隐藏的优点:

1.保护数据,防止程序直接访问数据,对数据进行不适当的修改

2.方便复用,隐藏实现细节,使用时只需调用接口

3.便于维护,实现细节与接口分离,修改细节时无需改变接口,修改调用程序时无需改变实现细节

1.7 封装

将实现细节放在一起,并将它们与抽象分开称为封装。

在这里插入图片描述

数据隐藏将类的公共接口与私有成员相分离,是一种封装。

将类函数定义和类声明放在不同的文件中也是一种封装。

2.类与对象
2.1 类成员函数的特点

1.定义成员函数时,使用作用域解析运算符(::)来标识函数所属的类。

Stock::update()就是函数的限定名。名称update()是限定名的缩写,只能在特定环境中使用。

2.成员函数可以访问类的私有成员。

3.成员函数被类对象调用,而不是直接函数调用。

2.2 内联函数

内联函数是指定义位于类声明中的函数。

如果要在类声明之外定义,则需要加上inline限定符:

inline void Stock::set_tot()
{
    ...
}

内联函数的规则要求在每个使用它们的文件中都对其进行定义。最简单的方法就是将内联定义放在类的头文件中。

2.3 对象的存储

类是用户定义的类型,对象是类的实例。

每个对象都存储自己的数据,但是共享类方法。

在这里插入图片描述

2.4 典型的类声明格式
class className
{
    private:
    	data member declarations
    public:
    	member function prototypes
};
3.构造函数和析构函数

构造函数的必要性:

1.用户定义类型需要有与基本型类似的初始化方式

2.类的私有成员数据被隐藏,无法在类外被直接赋值

3.需要使用类的成员函数对对象初始化

析构函数的必要性:

清理过期的对象

3.1 构造函数

功能:创建新对象,并自动对其进行初始化

特点:构造函数名称与类名相同,它没有返回值,也没有声明类型

1.声明

通过函数重载,可以创建多个同名的构造函数,只要每个函数的参数列表不同。

Stock::Stock(const string & co, long n, double pr)
{
    ...
}

注意:在构造函数中,不能将类成员名称用作构造函数的参数名

2.使用

//显式调用构造函数
Stock food = Stock("World Cabbage", 250, 1.25);
//隐式调用构造函数
Stock food("World Cabbage", 250, 1.25);
//构造函数与new一起使用
Stock *food = new Stock("World Cabbage", 250, 1.25);

3.默认构造函数

定义及作用:

默认构造函数是在未提供显式初始值时,用来创建对象的构造函数。

如果没有定义任何构造函数,编译器就会提供默认构造函数。

意义:

默认构造函数的存在,使得Stock stock1;这样的声明成为可能。否则,将会报错,原因是创建了一个没有初始化的对象。

定义默认构造函数的两种方式,两种方式不应该同时采用:

1.为已有构造函数的所有参数提供默认值

2.新创建一个没有参数的构造函数

区分各种调用方式:

Stock first;				//隐式调用默认构造函数
Stock first = Stock();		//显式调用默认构造函数
Stock *first = new Stock; 	//隐式调用默认构造函数

Stock first("Concrete");	//调用非默认构造器
Stock second();				//声明一个返回值为Stock类型的函数
stock third;				//调用默认构造器
3.2 析构函数

功能:清理过期的类对象

特点:

析构函数的名称是在类型前加上~,如Stock::~Stock()

析构函数没有返回值、声明类型、参数。

调用时机:

类对象过期时,会自动调用析构函数。

编译器决定在何时调用析构函数,通常不应该在代码中显式调用析构函数。

如果程序员没有提供析构函数,编译器将先隐式地声明一个默认析构函数,当发现导致对象删除的代码后,再提供析构函数的定义。

3.3 初始化与赋值中的构造与析构函数

1.初始化——隐式调用构造函数:

执行:创建一个名为stock1的Stock对象,并将数据成员初始化为指定的值。

Stock stock1("NanoSmart", 12, 20.0);

2.初始化——显式调用构造函数:

编译器有两种方式执行下面的语法:

第一种是创建一个名为stock2的Stock对象,并将数据成员初始化为指定的值。

第二种是使构造函数先创建一个临时对象,对临时对象初始化,然后将该临时对象相应成员的值复制到stock2中。之后,编译器会为该临时对象调用析构函数

Stock stock2 = Stock("Boffo Objects", 2, 20.0);

3.赋值——使用另一个对象:

默认情况下,将一个对象赋值给同一类型的另一个对象时,C++将源对象的每个数据成员复制到目标对象的相应数据成员中。

stock1 = stock2;

4.赋值——使用构造函数:

在对stock1赋新值时,构造函数先创建一个临时对象,对临时对象初始化,然后将其复制给stock1,随后对临时对象调用析构函数。

stock1 = Stock("Nifty Foods", 10, 50.0);
3.4 const成员函数

不修改调用对象的类方法,应该声明为const

//声明
void show() const;
//定义
void Stock::show() const 
{
    ...
}

只有const方法,才能被const对象调用,以保证该方法不会改变调用对象的值:

const Stock land = Stock("KKK");
land.show();
4. this指针

1.必要性:

设计一个成员函数,用于查看当前对象和输入的另一个对象,并返回股价较高的那个对象的引用。

const Stock & Stock::topval( const Stock & s ) const;

如果要返回对另一个对象的引用,只需返回s

如何返回对当前对象的引用?

2.对该函数一些说明:

访问方式:该函数隐式的访问调用对象,显式的访问输入的另一个对象。

引用的作用:

传入参数为引用,可以提高效率。返回类型为引用,说明返回的是对象本身,而不是其副本。

const的作用:

括号中的const表明,该函数不会修改显式访问的对象;

括号后的const表明,该函数不会修改隐式访问的对象;

由于该函数返回了两个const对象之一的引用,因此函数返回类型也为const引用。

3.this指针的作用:

为了实现该函数,this指针用于指向调用成员函数的对象

this指针中存储了对象的地址,所以*this是调用对象本身,可以作为调用对象的别名。

每个成员函数都有一个this指针,将其作为隐藏参数传递给方法。

4.该函数的实现:

const Stock & Stock::topval( const Stock & s ) const
{
    if(s.total_val > total_val)
        return s;
    else
        return *this;
}
5. 对象数组
Stock mystuff[4];
const int STKS = 10;
Stock stocks[STKS] = {
  Stock("NanoSmart", 12.5, 20),
  Stock(),
  Stock("Mono", 130, 3.25)
};

上述的声明只初始化了部分元素,其余的元素将使用默认构造函数进行初始化。

初始化对象数组的方案是:

使用默认构造函数创建数组元素;

花括号中的构造函数将创建临时对象

将临时对象的内容复制到数组元素中;

临时对象被析构函数删除。

如果要创建类对象数组,该类必须有默认构造函数。

例子:使用第4部分的函数,比较对象数组中值最大的元素:

const Stock *top = &stocks[0];
for(int i=1; i<STKS; i++)
	top = &top->topval(&stocks[i]);
6. 类作用域

类成员名称的作用域都为整个类,只在该类内已知,在类外不可知。因此,可以在不同类中使用相同的类成员名而不引起冲突。

只有在类声明成员函数定义中,可以使用非限定名。其它情况下,使用类成员名时,必须根据上下文使用直接成员运算符.、间接成员运算符->或作用域解析符::

6.1 作用域为类的常量

声明类只是描述了对象的形式,并没有创建对象。因此在创建对象前,将没有用于存储值的空间。

因此下面的做法并不可行:

private:
	const int Months = 12;
	double costs[Months];

两种可行的方法是:

enum{Months=12}; 这种方式声明枚举目的是创建符号常量,并不会创建枚举类型的类数据成员。Months只是一个符号名称,在作用域为整个类的代码中遇到它时,编译器将使用12替换它。

static const int Months = 12;这将创建一个名为Months的常量,该常量与其它静态变量存储在一起,而不存储在对象中。

7. 抽象数据类型

抽象数据类型(abstract data type,ADT)以通用的方式描述数据类型,而没有引入语言或实现细节。

抽象数据类型——栈的类声明

#ifndef STACK_H_
#define STACK_H_
typedef unsigned long Item;

class Stack
{
    private:
    	enum {MAX=10}; //声明类作用域内的符号常量
    	Item items[MAX];
    	int top;
    public:
    	Stack();
    	bool isempty() const;
    	bool isfull() const;
    	//返回值为false表示栈满,为true表示成功加入元素
    	bool push(const Item &item);
    	//返回值为false表示栈空,为true表示成功弹出元素
    	bool pop(Item & item);
}
#endif

私有部分指明了数据的存储方式。公有接口隐藏了数据的表示。私有部分数据存储方式的改变,不会影响到公有接口。

该类不是使用特定的类型来定义栈,而是使用通用的Item类型来描述。如果需要其它类型的栈,只需要修改typedef语句。而类声明和定义保持不变。

8. 练习题
8.1 什么是类?

类是用户定义的类型。

类声明指定了数据将如何存储,同时指定了用来访问操作这些数据的方法。

8.2 类如何实现抽象、封装和数据隐藏?

通过类的公有接口可以操作类的对象,这就是抽象。

类可以分为公共接口和私有数据两部分。也就是说公共接口和实现细节是分开的,体现了封装。

私有数据无法直接被访问,只能通过公共接口间接访问,从而实现了数据的隐藏。

8.3 对象和类的关系是什么?

类是用户定义的类型,对象是类的一个实例。

8.4 除了是函数之外,类函数成员和类数据成员之间的区别是什么?

同一个类的多个对象实例, 每个对象都单独存储自己的数据成员,但是所有对象都共享一组成员函数。

8.5 类的构造函数在何时被调用?类的析构函数?

类的构造函数在创建对象被显式调用时;

类的析构函数在对象过期时被调用。

8.6 什么是默认构造函数?使用默认构造函数有什么好处?

默认构造函数是没有参数所有参数都有默认值的构造函数。

使用默认构造函数,可以使声明的对象被隐式初始化 ,使得声明数组成为可能。

8.7 this*this是什么?

this是调用对象的地址;*this是调用对象本身, 是调用对象的别名。

只能通过公共接口间接访问,从而实现了数据的隐藏。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值