类和对象(一)

为什么要使用类:
C++基本语言定义的抽象数据类型:结构体,描述数据。全局函数,描述对数据的操作,数据以参数的形式传递给函数。
但是以上抽象数据类型存在以下问题:数据和操作之间的密切关系不能体现。结构体和操作之间的明显关联只是结构体数据类型的指针是这些函数的参数,使用时需要传递数据的地址,与内置类型相比不直观也不方便。还有就是如果我们大量使用全局函数容易引起名字的冲突。
后来就出现了类的思想:使结构体可以包含函数,称为成员函数,结构体中的数据则称为数据成员。这样扩展的结构体就被称为类,结构体类型的变量被称为对象。
C++允许用户以类的形式自定义数据类型,反映待解决问题中的各种概念,以更自然的方式编写程序。

在理解类之前,先来介绍几组概念:
类的基本思想是数据抽象和封装。
数据抽象是一种依赖接口和实现分离编程的技术,接口包括用户能执行的操作,实现是类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。(抽象数据类型由两部分组成:一组数据和对这组数据的操作)
封装是将数据和操作捆绑在一起并加上访问控制。
对象是数据和操作的封装体(类结构体类型变量)
对象是客观事物的抽象,类是一组具有相同属性和行为的对象的抽象,对象又称类的实例,数据描述对象的属性,操作描述对象的行为。

数据成员的类内初始化:
C++11 允许为数据成员提供一个类内初始值,创建对象时,类内初始值将用于初始化数据成员。
没有初始值的成员将被默认初始化。
数据成员类内初始值只能放在等号“=”右边,或者放在花括号“{}”里,不能使用圆括号“()”。

struct SalesData{
string productNo; 	//没有类内初始值,默认初始化为空串
double price = 0.0; 	//数据成员类内初始值为0
unsigned unitSold = 0;//初始值为0
…};
SalesData s; 
	//productNo 初始化为空串,price 和unitSold 都初始化为0
s .print(); 			//输出“:0 0 0”

当然类的成员函数也可以在类外定义:
如果使用函数类外定义,必须先在类内进行声明:
当调用类的成员函数时,就要引入类作用域的概念:
类作用域:
C++中每个类定义都引入了一个类作用域,类定义中声明的数据成员和成员函数都具有类作用域。
成员函数的类外定义语法:
类外定义的成员函数名字前面要加类名和作用域符“::”,表示这个函数是在其所属的类作用域内,是这个类的成员函数,不同于全局函数。
成员函数定义虽处于类定义的花括号外,但还是在类作用域内,可以自由访问类的成员,不需要成员访问语法。
类内定义与类外定义的区别:在类定义的花括号内定义的成员函数默认为inline 函数,在类外定义inline 成员函数,需要显式地在函数声明或定义前加关键字inline。
(inline函数是由inline关键字来定义,引入inline函数的主要原因是用它替代C语言中复杂易错不易维护的宏函数。)(这里我们不深入了解)

信息隐藏与访问限制和struct与class关键字定义的类:
当然数据成员过多和成员函数过多时容易造成无意修改或有些数据不希望用户看到(数据需要隐藏),为了保证安全性问题,我们可以通过限定成员的访问权限来实现信息隐藏。
关键字public、private 和protected 被称为访问限定符。
访问限定符在类定义中使用,一般语法为:

struct 类名
{
public:
公有成员声明;
private:
私有成员声明;
protected:
被保护成员声明;
};

访问限定符在类定义中的出现顺序和出现次数没有限制
一个访问限定符的作用会持续到出现下一个访问限定符或类定义结束
如果没有指定访问限定符,struct 成员的默认访问限制为public

C++引入了一个关键字class 来定义类
class 和struct 定义的类稍有区别
如果class的成员没有设置访问限定符,则默认为private

发送消息:
发送消息就是调用成员函数。
例如s.print()就是向对象s发送一个print()消息。
面向对象编程的主要工作就是创建一组对象并给它们发送消息。
对象能够持有的数据和接收的消息由它们所属的类决定。

对象的布局与类的存储:
每个对象都有自己的数据成员,类的成员函数并不在各个对象中存储,而是整个类存储一份,所有对象共享这些成员函数的定义。
一般情况下,一个访问限定符所限定的一组数据成员在内存中是连续存储的。不同的访问块本身不一定按照声明的顺序存储。
简单对象在内存中占据的存储空间是所有数据成员大小的和。(但是每个对象都是可区分的,当对象中没有数据成员时,其内存大小也至少为一)
但是如果为了访问效率而采用边界对齐的话,对象的大小是机器字长的整数倍。