顶一下 楼主威武
!
!
虚心诚学
2011.6.1 第12章 类 成员可以是数据、函数或类型别名。 构造函数初始化列表跟在构造函数的形参表之后,并以冒号开头。 在类内部,声明成员函数是必需的,而定义成员函数则是可选的。 类内部定义的函数默认为inline。 成员函数有一个附加的隐含实参——this指针。 const必须同时出现在声明和定义中。 类背后蕴含的基本思想是数据抽象和封装。 封装是一项将低层次的元素组合起来形成新的、高层次实体的技术。 一个访问标号可以出现的次数通常是没有限制的。 数据抽象和封装的两个重要有点: 避免类内部出现无意的、可能破坏对象状态的用户级错误。 随时间推移可以根据需求改变或缺陷报告来完善类实现,而无须改变用户级代码。 类可以定义自己的局部类型名字,将这个类型设为public,就允许用户使用这个名字。 成员函数只能重载本类的其他成员函数。 不在类定义体内定义的inline成员函数,其定义通常应放在有类定义的同一头文件中。 对于不完全类型,不能定义该类型的对象,只能用于定义指向该类型的指针及引用,或者用于声明使用该类型作为形参类型或返回类型的函数。 定义类型时不进行存储分配。 因为在类定义之后可以接一个对象定义列表。定义必须以分号结束。 成员函数不能定义this形参。 当我们需要将一个对象作为整体引用而不是引用对象的一个成员时,需要显式引用this。 const成员函数只能返回*this作为一个const引用。 可变数据成员永远都不能为const,const成员函数可以改变mutable成员。 定义类型的成员,如Screen::index(称为完全限定名),使用作用域操作符来访问。 返回类型出现在成员名字前面,如果返回类型使用由类定义的类型,则必须使用完全限定名。 在C++程序中,所有名字必须在使用之前声明。 如果类作用域中使用的名字不能确定为类成员名,则在包含该类或成员定义的作用域中查找,以便找到该名字的声明。 一旦一个名字被用作类姓名,该名字就不能被重复定义。 尽管类的成员被屏蔽了,但仍然可以通过用类名来限定成员名或显式使用this指针来使用它。 尽管全局对象被屏蔽了,但通过用全局作用域确定操作符(::)来限定名字,仍然可以访问。
2011.6.2 只要创建类类型的新对象,都要执行构造函数。 构造函数不能声明为const。 创建类类型的const对象时,运行一个普通构造函数来初始化该const对象。 构造函数可以定义在类的内部或外部。 构造函数初始化式只在构造函数的定义中而不是声明中指定。 构造函数分两个阶段执行:(1)初始化阶段 (2)普通的计算阶段 计算阶段由构造函数体中的所有语句组成。 类类型的数据成员总是在初始化阶段初始化。 初始化发生在计算阶段开始之前。 有些成员必须在构造函数初始化列表中进行初始化。对于这样的成员,在构造函数函数体中对它们赋值不起作用,没有默认构造函数的类类型成员,以及const或引用类型成员,都必须在构造函数初始化列表中进行初始化。 初始化const或引用类型数据成员的唯一机会是在构造函数初始化列表中。 不指定初始化执行的次序,成员被初始化的次序就是定义成员的次序。 按照与成员声明一致的次序编写构造函数初始化列表是个好主意。 尽可能避免使用成员来初始化其他成员。 一个类哪怕只定义了一个构造函数,编译器也不会再生成默认构造函数。 如果每个构造函数将每个成员设置为明确的已知状态,则成员函数可以区分空对象和具有实际值的对象。 如果定义了其他构造函数,则提供一个默认构造函数几乎总是对的。 初级C++程序员常犯的一个错误:Sales_item myobj(); 这是函数声明,不是一个对象。 正确做法:Sales_item myobj; 或者 Sales_item myobj = Sales_item(); 可以通过将构造函数声明为explicit,来防止在需要隐式转换的上下文中使用构造函数。 explicit关键字只能用于类内部的构造函数声明上,类的定义体外部所做的定义上不再重复。 任何构造函数都可以用来显式地创建临时对象。 通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为explicit。 友元(friend)机制允许一个类将对其非公有成员的访问权授予指定的函数或类。 只能出现在类定义的内部。 一般在开始或结尾。 友元可以是普通的非成员函数,或前面定义的其他类的成员函数或整个类。 必须先定义包含成员函数的类,才能将成员函数设为友元。另一方面,不必预先声明类和非成员函数来将它们设为友元。 现在看怎么这么饶,大概意思应该是对于友元是成员函数,应该先定义包含该成员函数的类;友元是类和非成员函数,则不必预先声明。 类必须将重载函数集中每一个希望设为友元的函数都声明为友元。 类可以定义类静态成员。 static数据成员独立于该类的任意对象而存在,它是与类关联的对象。 static成员函数没有this形参。 它可以直接访问所属类的static成员,但不能直接使用非static成员。 static成员是类的组成部分但不是任何对象的组成部分。 static成员函数不能被声明为const或虚函数。 static数据成员必须在类定义体的外部定义。 static成员在定义时进行初始化。 将static数据成员的定义放在包含类的非内联成员函数定义的文件中。 一旦成员名出现,static成员的定义就是在类作用域中。 static关键字只能用于类定义体内部的声明中,定义不能标示为static。 一般而言,类的static成员,像普通数据成员一样,不能在类的定义体中初始化。 例外是const static数据成员可以在类的定义体内初始化。 该数据成员仍必须在类的定义体之外进行定义,不必再指定初值。 通过将类的实现所用到的数据和函数设置为private来封装类。
2011.6.12 第13章 复制控制 如果没有显式定义复制构造函数或赋值操作符,编译器(通常)会为我们定义。 复制构造函数具有单个形参,该形参(常用const修饰)是对该类类型的引用。 复制构造函数最主要的识别特征! 当将该类型的对象传递给函数或从函数返回该类型的对象时,将隐式使用复制构造函数。 赋值操作符可以通过指定不同类型的右操作数而重载。 编译器自动实现复制控制(复制构造函数、赋值操作符和析构函数),也可以定义自己的版本。 当类具有指针成员,需要类定义自己的复制控制成员。 复制初始化首先使用指定构造函数创建一个临时对象,然后用复制构造函数将临时对象复制到正在创建的对象。 vector<string> svec(5); 容器这种构造方式使用了默认构造函数和复制构造函数。 对于类类型数组,如果希望不指定实参或指定多个实参,就需要使用完整的构造函数语法。 编译器在即使我们定义了其他构造函数,也会合成复制构造函数。 复制数组时合成复制构造函数将复制数组的每一个元素。 有些类必须对复制对象时发生的事情加以控制:1.类经常有一个数据成员是指针;2.一些类在创建新对象时必须做一些特定工作。 有些类需要完全禁止复制,类必须显式声明其复制构造函数为private。 如果连友元和成员中的复制也禁止,则声明private但不对其定义。 重载操作符是一些函数,名字为operator后跟着所定义的操作符的符号。 当操作符为成员函数时,它的第一个操作数隐式绑定到this指针。 内置类型的赋值运算返回对右操作数的引用。 赋值操作符的返回类型应该与内置类型赋值运算返回的类型相同。 合成赋值操作符返回*this,对左操作数对象的引用。 撤销类对象时会自动调用析构函数。 容器中的元素总是按逆序撤销。 三法则:如果类需要析构函数,则它也需要赋值操作符和复制构造函数。 这是一个经验法则。 合成析构函数并不删除指针成员所指向的对象。 不能重载析构函数。 即使编写了析构函数,合成析构函数仍然运行。 中间的消息处理示例跳过,管理指针成员看的迷迷糊糊,大概意思明白了,当时看这一章时,感受十分不好,现在写笔记回头看,思路清晰了许多,也不再赘述,是一种方法。 最后以书上的一句话结束本章:定义复制控制函数最为困难的部分通常在于认识到它们的必要性。 第14章 重载操作符与转换 保留字operator后接需定义的操作符符号。 重载操作符具有返回类型和形参表。 重载操作符的形参数目(包括成员函数的隐式this)与操作符的操作数数目相同。 函数操作符( operator() )可以接受任意数目的操作数。 重载操作符必须具有一个类类型或枚举类型操作数。 用于内置类型的操作符,其含义不能改变。 不能为任何内置类型定义额外的新的操作符。 操作符的优先级、结合性或操作数数目不能改变。 +,-,*和&取决于操作数数目。 函数调用操作符之外,重载操作符时使用默认实参是非法的。 重载操作符并不保证操作数的求值顺序。 作为类成员的重载函数,隐含的this形参,限定为第一个操作数。 操作符定义为非成员函数时,通常必须将它们设置为所操作类的友元。 如果重新定义&&和||,将失去操作符的短路求值特征。 重载逗号、取地址、逻辑与、逻辑或等操作符通常不是好做法。 当一个重载操作符的含义不明显时,给操作取一个名字更好。对于很少用的操作,使用命名函数通常也比用操作符更好。 复合赋值操作符通常应定义为类的成员。 自增、自减和解引用应定义为类成员。 对称的操作符,如算术操作符、相等操作符、关系操作符和位操作符,最好定义为普通非成员函数。 输出操作符<<返回类型是一个ostream引用。 一般而言,输出操作符应输出对象的内容,进行最小限度的格式化,它们不应该输出换行符。让用户自己控制输出细节。 必须使输出或输入操作符称为非成员操作符。 输入操作符必须处理错误和文件结束的可能性。 输入期间的错误: (1)任何读操作都可能因为提供的值不正确而失败。这次的读入以及流的后续使用都将失败。 (2)任何读入都可能碰到输入流中的文件结束或其他一些错误。 设计输入操作符时,如果可能,要确定错误恢复措施,这很重要。 通常错误最好留给IO标准库自己来指出。 为了与内置操作符保持一致,加法返回一个右值,而不是一个引用。 这里总结一下:对于重载操作符,要遵循标准库定义的习惯,保持使用习惯上的统一。 相等和不等操作符一般应该相互联系起来定义,让一个操作符完成比较对象的实际工作,而另一个操作符只是调用前者。 赋值操作符必须定义为成员函数,且必须返回对*this的引用。 一般而言,赋值操作符与复合赋值操作符应返回左操作数的引用。 下标操作符必须定义为类成员函数。 一般需要定义两个版本,一个为非const成员并返回引用,另一个为const成员并返回const引用。 这里讨论一下书上的例子: int &operator[] (const size_t); const int &operator[] (const size_t) const; 重载的判决因素应该有该成员函数是否为const。 箭头操作符必须定义为类成员函数。 必须返回指向类类型的指针,或者返回定义了自己的箭头操作符的类类型对象。 解引用操作符不要求定义为成员。 自增/自减操作符的前缀与后缀的区别方法: 后缀式操作符函数接受一个额外的(无用的)int型形参。 使用后缀式操作符时,编译器提供0作为这个形参的实参。 为了与内置类型一致,前缀式操作符应返回被增量或减量对象的引用,后缀式操作符应返回旧值,且应作为值返回。 一般而言,最好前缀式和后缀式都定义。 函数调用操作符必须声明为成员函数。一个类可以定义函数调用操作符的多个版本,由形参的数目或类型加以区别。 定义了调用操作符的类,其对象常称为函数对象,即它们是行为类似函数的对象。 函数对象经常用作通用算法的实参。 标准库函数对象类型在functional头文件中定义。 每个标准库函数对象类表示一个操作符。 给出一个使用例子: sort(svec.begin(), svec.end(), greater<string>); 函数适配器种类: 绑定器(binder),它通过将一个操作数绑定到给定值而将二元函数对象转换为一元函数对象。 bind1st, bind2nd 名字表现出绑定的实参位置。 求反器(negator),它将谓词函数对象的真值求反。 not1, not2 名字表现出对几元函数对象的真值求反。 一个类可以定义自己的转换,应用于其类类型对象。 转换操作符是一种特殊的类成员函数。 operator type(); 转换函数必须是成员函数,不能指定返回类型,并且形参表必须为空。 转换函数一般不应该改变被转换的对象。因此,转换操作符通常应定义为const成员。 类类型转换之后不能再跟另一个类类型转换。 之后内容跳过。