可能是因为类这块的内容比较多,所以,总觉得很难,,,
每次遇到看一本书不能理解的内容时,总喜欢,同时看好几本书的该内容,因为说明的角度或者理解的角度不一样,
或者看的次数多了,
到最后到都能理解了~
类定义了数据成员和函数成员:数据成员用于存储与该类类型的对象相关联的状态,而函数成员则负责执行赋予数据意义的操作。通过类我们能够将实现和接口分离,用接口指定类所支持的操作,而实现的细节只需类的实现者了解或关心。这种分离可以减少使编程冗长乏味和容易出错的那些繁琐工作。 vector,istream 和 string,都是类类型。每个类可以没有成员,也可以定义多个成员,成员可以是数据、函数或类型别名。 创建一个类类型的对象时,编译器会自动使用一个构造函数(第 2.3.3 节)来初始化该对象。构造函数是一个特殊的、与类同名的成员函数,用于给每个数据成员设置适当的初始值。 在类内部,声明成员函数是必需的,而定义成员函数则是可选的。在类内部定义的函数默认为 inline(第 7.6 节)。 在类外部定义的成员函数必须指明它们是在类的作用域中。将关键字 const 加在形参表之后,就可以将成员函数声明为常量: double avg_price() const;
const 成员不能改变其所操作的对象的数据成员。const 必须同时出现在声明和定义中,若只出现在其中一处,就会出现一个编译时错误。 类背后蕴涵的基本思想是数据抽象和封装。 数据抽象是一种依赖于接口和实现分离的编程(和设计)技术。封装是一项低层次的元素组合起来的形成新的、高层次实体珠技术。函数是封装的一种形式:函数所执行的细节行为被封装在函数本身这个更大的实体中。程序的所有部分都可以访问带有 public 标号的成员。 类型的数据抽象视图由其 public 成员定义。
使用类的代码不可以访问带有 private 标号的成员。private 封装了类型的实现细节。 数据抽象和封装提供了两个重要优点:
• 避免类内部出现无意的、可能破坏对象状态的用户级错误。
• 随时间推移可以根据需求改变或缺陷(bug)报告来完美类实现,而无须改变用户级代码。
关键字inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用。
将类定义在头文件中,可以保证在每个使用类的文件中以同样的方式定义类。使用头文件保护符(header guard)(第 2.9.2 节),来保证即使头文件在同一文件中被包含多次,类定义也只出现一次。
只要类名一出现就可以认为该类已声明。因此,类的数据成员可以是指向自身类型的指针或引用:
class LinkScreen {
Screen window;
LinkScreen *next;
LinkScreen *prev;
}
定义了一个新的类型,但没有进行存储分配。当我们定义一个对象 Sales_item item; 时,编译器分配了足以容纳一个 Sales_item 对象的存储空间。成员函数具有一个附加的隐含形参,即指向该类对象的一个指针。这个隐含形参命名为 this,与调用成员函数的对象绑定在一起。成员函数不能定义 this 形参,而是由编译器隐含地定义。
尽管在成员函数内部显式引用 this 通常是不必要的,但有一种情况下必须这样做:当我们需要将一个对象作为整体引用而不是引用对象的一个成员时。
myScreen.move(4,0).set('#');
这个语句等价于:
myScreen.move(4,0);
myScreen.set('#');
这些函数的返回类型是 Screen&,指明该成员函数返回对其自身类类型的对象的引用。每个函数都返回调用自己的那个对象。使用 this 指针来访问该对象。下面是对两个新成员的实现:
Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
Screen& Screen::move(index r, index c)
{
index row = r * width; // row location
cursor = row + c;
return *this;
}
函数中唯一需要关注的部分是 return 语句。在这两个操作中,每个函数都返回 *this。在这些函数中,this 是一个指向非常量 Screen 的指针。如同任意的指针一样,可以通过对 this 指针解引用来访问 this 指向的对象。
在普通的非 const 成员函数中,this 的类型是一个指向类类型的 const 指针(第 4.2.5 节)。可以改变 this 所指向的值,但不能改变 this 所保存的地址。在 const 成员函数中,this 的类型是一个指向 const 类类型对象的 const 指针。 既不能改变 this 所指向的对象, 也不能改变 this 所保存的地址。
可变数据成员(mutable data member)永远都不能为 const,甚至当它是 const 对象的成员时也如此。因此,const 成员函数可以改变 mutable 成员。
要将数据成员声明为可变的,必须将关键字 mutable 放在成员声明之前:
this 指针,inline函数,mutable(可变数据成员)!!!!!!!!!!!
Class obj; // Class is some class type
Class *ptr = &obj;
// member is a data member of that class
ptr->member; // fetches member from the object to which ptr points
obj.member; // fetches member from the object named obj
即:(1)指针->成员
(2)类对象.成员
Sales_item(): units_sold(0), revenue(0.0) { }
private:
std::string isbn;
unsigned units_sold;
double revenue;
};
这个构造函数使用构造函数初始化列表来初始化 units_sold 和 revenue 成员。isbn 成员由 string 的默认构造函数隐式初始化为空串。 构造函数的名字与类的名字相同,并且不能指定返回类型。像其他任何函数一样,它们可以没有形参,也可以定义多个形参。
Sales_item::Sales_item(const string &book): isbn(book), units_sold(0), revenue(0.0) { }
构造函数初始化列表以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化式。这个构造函数将 isbn 成员初始化为 book 形参的值,将 units_sold 和 revenue 初始化为 0。与任意的成员函数一样,构造函数可以定义在类的内部或外部。构造函数初始化只在构造函数的定义中而不是声明中指定。
按照与成员声明一致的次序编写构造函数初始化列表是个好主意。此外,尽可能避免使用成员来初始化其他成员。
隐式类类型转换。
可以通过将构造函数声明为 explicit,来防止在需要隐式转换的上下文中使用构造函数: explicit 关键字只能用于类内部的构造函数声明上。在类的定义体外部所做的
定义上不再重复它: 通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为 explicit。将构造函数设置为 explicit 可以避免错误,并且当转换有用时,用户可以显式地构造对象。
尽管大多数对象可以通过运行适当的构造函数进行初始化,但是直接初始化简单的非抽象类的数据成员仍是可能的。 对于没有定义构造函数并且其全体数据成员均为 public 的类,可以采用与初始化数组元素相同的方式初始化其成员:
显式初始化类类型对象的成员有三个重大的缺点。
1. 要求类的全体数据成员都是 public。
2. 将初始化每个对象的每个成员的负担放在程序员身上。 这样的初始化是乏
味且易于出错的,因为容易遗忘初始化式或提供不适当的初始化式。
3. 如果增加或删除一个成员,必须找到所有的初始化并正确更新。 定义和使用构造函数几乎总是较好的。当我们为自己定义的类型提供一个默认构造函数时,允许编译器自动运行那个构造函数,以保证每个类对象在初次使用之前正确地初始化。
友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类。友元的声明以关键字 friend 开始。它只能出现在类定义的内部。友元声明可以出现在类中的任何地方:友元不是授予友元关系的那个类的成员。
有元::
友元可以是普通的非成员函数, 或前面定义的其他类的成员函数, 或整个类。将一个类设为友元, 友元类的所有成员函数都可以访问授予友元关系的那个类的非公有成员。
类可以定义类 静态成员,而不是定义一个可普遍访问的全局对象。
static成员:
static 成员是类的组成部分但不是任何对象的组成部分,因此,static 成员函数没有 this 指针。 通过使用非 static 成员显式或隐式地引用 this 是一个编译时错误。
因为 static 成员不是任何对象的组成部分,所以 static 成员函数不能被声明为 const。毕竟,将成员函数声明为 const 就是承诺不会修改该函数所属对象。