C++ 的对象和类

2014/11/20 01:14 am posted in  C++

C++通过改进C语言,使得应用更容易,最重要的OOP特性:

  • 抽象
  • 封装和数据隐藏
  • 多态
  • 继承
  • 代码的可重用性

为了把他们组合在一起,C++所做的最重要的改进是提供了类。

未开发一个类并编写一个使用它的程序,通常,C++程序员将结构(类定义)放在头文件中,并将实现(类方法的代码)放在源代码文件中。

为了帮助识别类,常见但不通用的约定,将类名首字母大写。

访问控制

类中包含private,public和protected。

类设计尽可能将共有接口与实现细节分开。公有接口表示设计的抽象组件。将实现细节放在一起并将它们与抽象分开被称为封装。数据的隐藏(将数据放在类的私有部分中)是一种封装,将实现的细节隐藏在私有部分中。封装的另一个例子是,将类函数定义和类声明放在不同的文件中。

数据隐藏不仅可以防止直接访问数据,还让开发者无需了解数据是如何被表示的。在对后期程序修改的时候,无需修改程序接口,这使程序维护起来更容易。

由于隐藏数据是OOP主要目标之一,因此数据项通常放在私有部分,组成类接口的成员函数放在公有部分。通常程序员使用私有成员函数来处理不属于公有接口的实现细节。

其实类和结构唯一区别就是结构默认的访问类型是public,而类为private。

实现类成员函数

除了类的描述(class部分),还需要创建类描述的第二部分:为那些由类声明中的原型表示的成员函数提供代码。成员函数定义与常规函数非常相似,它们有函数头和函数体等等。但是:

  • 定义成员函数时,使用作用域解析运算符来表示函数所属的类
  • 类方法可以访问类的private组件

定义在类声明中的函数会自动生成内联函数,当然在类声明之外定义的成员函数,也可以使用inline使之成为内联函数。

所创建的每个新的对象都有自己的存储空间,用于存储其内部变量和成员。但同一个类的所有对象共享同一组类方法。在OOP中,调用成员函数被称为发送消息,因此将同样的消息发送给两个不同的对象将调用同一个方法,但该方法被用于两个不同的对象。

使用类

C++的目标是使得使用类与使用基本的内置类型(int等)尽可能相同。要创建类对象,可以声明类变量,也可以使用new对类对象分配存储空间。可以将对象作为函数的参数和返回值,也可以将一个对象赋值给另一个。C++提供了一些工具可用于初始化对象,让cin和cout识别对象,甚至在相似的类对象之间进行自动类型转换。

OOP程序员常依照客户/服务器模型进行讨论程序设计,客户是使用类的程序。类声明(包括类方法)构成了服务器,服务器的责任是确保服务器根据该接口可靠并准确地执行。服务器设计人员只能修改类设计的细节而不能修改端口。这样对服务器的修改不会客户的行为造成意外的影响。

类的构造函数和析构函数

构造函数

C++提供了一个特殊的成员函数——类构造函数,专门用于构造新对象、将值付给他们的数据成员。更准确地说,C++为这些成员函数提供了名称和使用语法,而程序员需要提供方法定义。

构造函数名称与类名相同,注意好参数列表,可以重载。

注意参数名不要和private数据名相同(否则无法赋值),一般采取加前缀的方式。

使用构造函数一般有两种方式,一是显式使用(直接调用构造函数,用好参数),另一种是隐式使用。

class S
{
...
}

//显式使用
S a = S(...);

//隐式使用
s a(...);

//使用new
S *p = new S(...);

若未提供显式初始化值会调用默认构造函数,但不会初始化其成员。

如果没有自定义默认构造函数,编译器会提供一个没有任何操作的默认构造函数。

定义默认构造函数有两种,一是给已有构造函数的所有参数提供默认值;二是通过函数重载定义另一个没有参数的构造函数。

因为只能有一个默认构造函数,因此不要同时出现以上两种方式。用户自定义默认构造函数,一般是给所有成员初始化><。

隐式使用默认构造函数时不要使用括号,画色添足,成声明一个函数了。

析构函数

在构造函数创建对象后,程序负责跟踪该对象,直到过期为止。

对象过期之后,程序将调用死神函数析构函数。析构函数完成清理工作。其实只有在动态内存上,析构函数才有更实际的意义。

比如,同new开辟的内存,析构函数用delete来释放,其他情况,基本用不到析构函数= =,说的好不负责任。。。。

析构函数和构造函数差不多,只不过前面加了一个~。如果没有手动申明一个析构函数的话,编译器会自动加上析构函数,当然,里面什么都没有,如果自定义类没有涉及到动态内存的话,其实这样也挺好。手懒的表现。

什么时候调用析构函数是由编译器决定的,一般是和栈一样,先入后出。

C++11的初始化列表一样可以用在对象的初始化上,不过列表里的内容是某构造函数的参数列表。

对于类方法不会修改到被调用对象时,应该在函数后面添加const。const能用就用, 不只是安全!

this指针

因为在类方法中,终会遇到需要返回或者比较调用对象的信息,那么this指针应时而生,他就是一个指针,指向了当前的调用对象,用法和指针一样。

对象数组

C++目的就是使得程序员自定义构造的类可以和既有的数据类型使用,因此,可以用对象创建数组等稍微高级点的数据结构。对象数组的初始化方式有点类似结构体,但是花括号内是调用构造函数(显示调用)或者直接什么都不写,默认构造函数构造。(如果是C++11的话,可以直接用初始化列表,这样更像结构体数组了)。

类作用域

C++自从有了类,就有了一种特殊的作用域。类内的元素,作用域只在类内,也就是说只对类内可见。这就是为什么在定义成员函数时必须使用作用域解析运算符。

因为有了作用域,也就有了作用域为类的常量,如果想用一个const常量来控制数组的长度,这是行不通的,因为在类中声明的const常量要在编译时就要分配空间,而建立类的时候,只是描述这个类而没有分配空间,所以常量的值是未知的,则数组的长度也是未知的。

解决方法有两个,一个是用枚举,一个是用static const,全局常量。

抽象数据类型(ADT)

类很适合描述ADT,公有成员函数接口提供了ADT描述的服务,类的私有部分和类方法的代码提供了实现,这些实现对类的客户隐藏。