您的位置:首页 > 编程语言 > C语言/C++

《C++ Primer Plus》(第6版)中文版—学习笔记—对象和类

2020-08-08 10:10 896 查看

第10章 对象和类

书中课后练习将在https://github.com/linlll/CppPrimePlus发布

过程性编程和面向对象编程

传统的C语言是过程性编程,当程序编译成可执行文件的时候,程序是根据语句一步步去运行的,自上而下,注重的是过程。如果程序要求我们对一个对象输入这个对象的属性,比如我有五个孩子,要求编写程序输入它们的身高、体重、BMI等消息,显然BMI需要通过函数来计算,这样当我们需要输入的时候,必须一步步的重新输入。而面向对象编程从数据本身出发,挖掘这个对象可以拥有多少数据已经操作。

抽象和类

class World
{
private:
int mass;
char name[20];
void count() { mass++ };
public:
void tellall(void);
}

上面的一段就是一个简单的类声明了,从此展开类的相关知识。

关键字private和public,他们分别表示了类对象中的私有部分和公有部分,顾名思义,私有部分对于类对象是不可见的,即不能够访问,而公有部分却可以访问,还有一点就是私有部分却可以由公有部分访问,这就有点想数据的作用域了,好比作用域为类对象一样,同一个类对象中,公有部分可以访问私有部分。

private是可以省略的,编译器会默认将不在public中的成员设置为private,例如

class World
{
int mass;						// private by default
char name[20];					// private by default
void count() { mass++ };		// private by default
public:
void tellall(void);
}

而上述的代码中,类对象中的成员函数是没有定义的,这往往放在头文件中,而函数定义需要限定符,比如

void World::tellall(void)
{
...
code is here
...
}

看到上述的代码中,private中有个函数

void count() { mass++ }
,在类对象中的成员函数中如果调用它,这个函数的作用就相当于内联函数一样,这个函数放在private中,意思是外部不能访问,只有自己的公有成员函数可以使用。当成员函数要用到很长的重复代码时候,也可以使用这种功能提高代码重用性,提高编程效率。

这里需要注意的是,如果我们在主函数中定义了两个类,那当我们要使用成员函数的时候,是有两个函数呢,还是只有一个公共的函数,答案是只有一个,例如我们创建了a和b的成员,成员函数都有update()函数,当我们使用a.update()和b.update()函数的时候,这个update()其实都是调用一个内存块里面的函数,只是这些这些代码用在不同的数据而已。

类的构造函数和析构函数

到现在为止我们创建类对象的时候,还不能想别的数据类型一样初始化,对于类对象而言,由于有关键字private的存在,我们不能在别的代码块中访问类对象的私有部分。这就有了构造函数,构造函数的名称必须与类名一致,它有个特征,它没有像普通函数一样需要返回值,甚至不需要void类型来声明,也就是说,构造函数是没有声明类型的。声明和定义如下

class A
{
private:
int a_;
public:
A();
A(int a)
}
A::A()
{
a_ = 0;
}
A::A(int a)
{
a_ = a;
}

我们可以看到,类A中有了一个构造函数声明,另外使用作用域运算符定义了一个同名的构造函数,用构造函数来初始化a_,还有一个特点,那就是构造函数中的参数不能够与类对象中的成员同名,一般而言,有两种方式命名,一种是在变量面前加上m_前缀,另一种是加上_后缀,正如上述代码所示。

代码中可以看到构造函数有两个,一个是带参数的,一个是没有带参数的,没有带参数的就是默认构造函数,之前介绍类的时候,并没有默认构造函数,其实编译器并没有不执行它,只是隐式的,没有任何操作,现在我们显式的写出来了,也就是说我们可以进行修改,那么应该如何使用他们呢,请看下面的代码

A one = A(12);			// using constructor
A two(12);				// using constructor
A three = new A(12);	// using constructor
A four;					// using default constructor

接下我们来谈谈析构函数,析构函数是用来“结束类的生命的“,构造函数负责构造,而析构函数负责清理,当对象的生命周期到了的时候,析构函数就会被调用,程序会决定什么时候对对象进行销毁。他的命名方式也比较特殊,是在构造函数前面加上~符号,如

~A()
,它也不需要返回值,也不需要用void声明类型。

关于析构函数,请看原文的程序,利用程序在编译器中进行调试,你可以看出析构什么时候会被调用。

const成员函数

const A one = A(12);
one.update(23);	// Error!!

由于类对象one被声明为了const类型,当调用update的时候,对于编译器来说,难以确保update函数会不会对变量a_进行修改,所以这种方式不被允许的。C++的解决方法,是将const关键字放在函数的括号后面,这样代码就是

class A
{
private:
int a_;
public:
void print(int a) const
{
std::cout << a_ << endl;
}
}

this指针

this指针就是指向自己的一个指针,而*this就是类对象本身,这有什么用呢,当我们想要比较两个对象的某个参数,并且想要返回最大的那个对象,那我们那什么返回呢,这个时候就要用到this了,指向类对象本身。

对象数组

const int STKS = 10;
Stock stocks[STKS] = { Stock(), Stock("LIN", 12.9, 10) };

类作用域

C++中引入了一个新的概念,类作用域,其实这个作用域在介绍类对象的时候我们应该就有了认知,比如我们不能访问类对象中的私有成员,而且不能直接调用成员函数,必须根据上下文使用直接成员运算符(.)、间接成员运算符(->)或作用域解析运算符(:😃,这些都是我们之前遇到过的。

那作用域里面能用常量吗,比如下述代码

class A
{
private:
const int MONTHS= 12;
int costs[MONTHS];
}

这样是不可以的,因为,这里的代码只是类的声明,并没有创建类,如果是可行,那每次在创建类的时候,都会产生一个常量,便会失去常量的意义。那C++中提供了两种方式实现这种做法

第一种方法是在类中声明一个枚举,用枚举这种方式并不会创建类数据成员。也就是说,所有对象中都不包含枚举。

另一种方式就是使用关键字static,由于static常量的作用域为全局。

class A
{
private:
enum { MONTHS= 12 };
int costs[MONTHS];
}

class B
{
private:
static const int MONTHS = 12;
int costs[MONTHS];
}
enum egg  { Small, Medium, Large, Jumbo };
enum t_shirt { Small, Medium, Large, Jumbo };
enum class egg  { Small, Medium, Large, Jumbo };
enum class t_shirt { Small, Medium, Large, Jumbo };
egg choice = egg::Large;
t_shirt Floyd = t_shirt::Large;

C++还允许提供作用域内枚举,看上述代码。

抽象数据类型

介绍了栈,先进后出。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: