使用函数指针指向重载的函数时,指针的类型必须与重载函数的一个版本精确匹配
119.标准库类型不允许做复制或赋值动作(IO对象不可复制或赋值)
只有支持复制的元素类型可以存储在vector或其他容器类型中
形参或返回类型也不能为流类型
如果需要传递或返回IO对象,则必须传递或返回指向该对象的指针或引用
一般情况下,如果要传递IO对象以便对它进行读写,可用非const引用的方式传递这个流对象
对IO对象的对象会改变它的状态,因此引用必须是非const的
120.流的状态由bad、fail、eof和good操作揭示。如果bad、fail或者eof的任意一个为true,则检查流本身将显示该流处于错误状态。类似地,如果这三个条件没有一个为true,则good操作将返回true。
121.输出缓冲区的刷新:
程序正常结束
缓冲区已满,下次写即会刷新
操作符ends, endl, flush显示刷新
unitbuf/nounitbuf设置流的内部状态,从而清空缓冲区
将输出流与输入流关联起来,如此,读取输入时即会刷新其关联的输出缓冲区
122.如果程序员需要重用文件流读写多个文件,必须在读另一个文件之前调用clear清除该流的状态
123.在打开文件时,无论是调用open还是以文件名作为流初始化的一部分,都需指定文件模式
文件流构造函数和open函数都提供了默认实参设置文件模式
从效果来看,为ofstream对象指定out模式等效于同时指定了out和trunc模式,对于用ofstream打开的文件,要保存文件中已存在的数据,唯一方法是显示地指定app模式打开
124.模式是文件的属性而不是流的属性
默认情况下,fstream对象以in和out模式同时打开。当文件同时以in和out打开时不清空,若只使用out模式,则文件会清空已存在的数据
125.stringstream对象不能使用open和close函数,而fstream对象则不允许使用str
126.stringstream提供转换和格式化,使用输入操作符时,空白符或换行符等将会被忽略
127.泛型:这些算法可作用于各种不同的容器类型;而这些容器又可以容纳各种不同类型的元素
128.所有的容器都定义了默认构造函数。为了使程序更清晰、简短,容器类型最常用的构造函数是默认构造函数。在大多数的程序中,使用默认构造函数能达到最佳运行时性能,并且使容器更容易使用
129.接收容器大小做形参的构造函数只适用于顺序容器,而关联容器不支持这种初始化
130.容器元素类型必须满足下面两个约束:
元素类型必须支持赋值运算
元素类型的对象必须可以复制
引用不支持一般意义的赋值运算,因此没有元素是引用类型的容器,其他的内置或复合类型则都可用做元素类型
除输入输出标准库类型之外,所有其他标准库类型都是有效的容器元素类型。特别地,容器本身也满足上述要求,因此,可以定义元素本身就是容器类型的容器。
131.在容器嵌套定义中,必须用空格隔开两个相邻的>符号,以示这是两个分开的符号,否则,系统会认为>>是单个符号,为右移操作符,并导致编译时错误
132.vector和deque的迭代器支持算术运算和关系运算,而list容器的迭代器则不支援
133.任何insert或push操作都可能导致迭代器失效。当编写循环将元素插入到vector或deque容器中时,程序必须确保迭代器在每次循环后都得到更新。
134.所有容器都通过比较其元素对来实现关系运算
135.resize操作可能会使迭代器失效。在vector或deque容器上做resize操作有可能会使其所有的迭代器都失效
对于所有的容器类型,如果resize操作压缩了容器,则指向已删除的元素的迭代器失效
136.vector容器不支持pop_front操作
137.erase、pop_front和pop_back函数使指向被删除元素的所有迭代器失效。对于vector容器,指向删除点后面的元素的迭代器通常也会失效。而对于deque容器,如果删除时不包括第一个元素或最后一个元素,那么该deque容器相关的所有迭代器都会失效。
138.赋值和assign操作使左操作数容器的所有迭代器失效。swap操作则不会使迭代器失效。
完成swap操作后,尽管被交换的元素已经存放在另一容器中,但迭代器仍然指向相同的元素。
swap操作:操作数必须是相同类型的容器,而且所存储的元素类型也必须相同。
139.为了使vector容器实现快速的内存分配,其实际分配的容量要比当前所需的空间多一些
140.vector的每种实现都可自由地选择自己的内存分配策略。然而,它们都必须提供reserve和capacity函数,而且必须是到必要时才分配新的内存空间。分配多少内存取决于其实现方式。不同的库采用不用的测路额实现。
141.元素是否连续存储还会显著的影响:
在容器的中间位置添加或删除元素的代价
执行容器元素的随机访问的代价
142.一般来说,除非找到选择使用其他容器的更好理由,否则vector容器都是最佳选择。
143.对于键类型,唯一的约束就是必须支持<操作符,至于是否支持其他的关系或相等运算,则不作要求
144.关联容器中map的类型是指key-value对的类型,而set则只是单纯的键的集合,value_type与key_type相同
145.multimap以及multiset中键都不是唯一的,因此每次调用insert总会添加一个元素
参数不同会导致删除的元素性质不同,带有一个键参数的erase版本将删除拥有该键的所有元素,并返回删除元素的个数,而带有一个或一对迭代器参数的版本则只删除指定的元素,并返回void
146multimap以及multiset中,如果某个键对应多个实例,则这些实例在容器中将相邻存放
147.在关联容器中,*解引用操作符返回的是value_type的值,对于map则是key-value;而[ ]作用符则不同,返回的mapped_type的值,及key所对应的value
148.算法永不执行容器提供的操作,只是依赖迭代器和迭代器的操作实现
算法不直接修改容器的大小。如果需要添加或删除元素,则必须使用容器操作
149.插入迭代器front_inserter(迭代器适配器)的使用将导致元素以相反的次序出现在目标对象中
例:front_inserter(ilist);
150.ostream_iterator对象必须与特定的流绑定在一起;istream_iterator则可在创建时不提供实参,此时该迭代器指向超出末端位置
151.流迭代器的限制:
1‘不可能从ostream_iterator对象读入,也不可能写到istream_iterator对象中
2’一旦给ostream_iterator对象赋了一个值,写入就提交了。赋值后,没有办法再改变这个值。此外,ostream_iterator对象中每个不同的值都只能正好输出一次
3‘ostream_iterator没有->操作符
152.对于反向迭代器reverse_iterator,++运算将访问前一个元素,而--运算则访问下一个元素
流迭代器不能创建反向迭代器
153.反向迭代器用于表示范围,而所表示的范围是不对称的[ ),这个事实可推导出一个重要的结论:使用普通的迭代器对反向迭代器进行初始化或赋值时,所得到的迭代器并不是指向原迭代器指向的元素(详见图11-2)
154.五种迭代器:
Input Iterator: 只读向前遍历的迭代器。例如:istream
Output Iterator: 只写向前遍历的迭代器。例如:ostream, inserter
Forward Iterator: 可读可写向前遍历的迭代器。泛型算法replace需要至少是前向迭代器
Bidirectional Iterator: 可读可写双向遍历迭代器。例如:list, set, multiset, map, multimap
Random Access Iterator: 可读可写随机访问迭代器。例如:vector, deque, string, array
尽管map和set类型提供双向迭代器,但关联容器只能使用算法的一个子集。问题在于:关联容器的键是const对象。因此,关联容器不能使用任何写序列元素的算法。只能使用与关联容器绑在一起的迭代器来提供用于读操作的实参
向算法传递迭代器时需注意,要传递功能达到算法要求的才行
155.区别带有一个值或以个谓词函数参数的算法版本 _if
区别是否实现复制的算法版本 _copy
156.与对应的泛型算法不同,list容器特有的操作能添加和删除元素(merge, remove, reverse, sort, splice, unique)
157.类--实现与接口分离
158.在类内部定义的函数默认为inline
159.类的成员函数都有一个附加的隐含实参this
160.const成员不能改变其所操作对象的数据成员
161.可以在任意的访问标号出现之前定义类成员,如果类是struct关键字定义的,则在第一个访问标号之前的成员是公有的;如果类是用class关键字定义的,则这些成员是私有的。
162.inline成员函数可以在类内或类外定义,但像其他inline一样,inline成员函数的定义必须在调用该函数的每个源文件中是可见的。不在类定义体内定义的inline成员函数,其定义通常应放在有类定义的同一头文件中
163.因为只有当类定义体完成后才能定义类,因此类不能具有自身类型的数据成员。然而,只要类名一出现就可以认为该类已声明。因此,类的数据成员可以是指向自身类型的指针或引用(类的前向声明一般用来编写相互依赖的类)
164.尽管在成员函数内部显示引用this通常是不必要的,但有一种情况下必须这样做:当我们需要将一个对象作为整体引用而不是引用对象的一个成员时。
最常见的情况是在这样的函数中使用this:该函数返回对调用该函数的对象的引用
165.不能从const成员函数返回指向类对象的普通引用。const成员函数只能返回*this作为一个const引用。非const成员函数中,this是一个指向类类型的const指针;而在const成员函数中,this的类型是一个指向const类类型对象的const指针。
166.同时基于const对象或非const对象的成员函数可以以重载实现。
167.可变数据成员mutable永远都不能成为const,它可以在const成员函数中被改变。
168.尽管在类中同名的全局对象被屏蔽了,但通过用全局作用域确定操作符来限定名字,仍然可以使用它。
169.不论成员是否在构造函数初始化列表中显式初始化,类类型的数据成员总是在初始化阶段初始化。初始化发生在计算阶段开始之前。
170.没有默认构造函数的类类型的成员,以及const或引用类型(无论是哪种类型)的成员,都必须在构造函数初始化列表中进行初始化。因为const对象或引用类型的对象可以初始化,但不能对它们赋值。
171.构造函数初始化列表仅指定用于初始化成员的值,并不指定这些初始化执行的次序。成员被初始化的次序就是定义成员的次序。按照与成员声明一致的次序编写构造函数初始化列表是个好主意。此外,尽可能避免使用成员来初始化其他成员。
172.只有当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数。
173.类通常应定义一个默认构造函数,而在默认构造函数中给成员提供的初始值应该指出该对象是“空”的
174."A myobj;"等价于“A myobj = A()”定义了类的对象,但“A myobj()”则是定义了类的一个函数
175.可以用单个实参来调用的构造函数定义了从形参类型到该类类型的一个隐式转换
176.explicit关键字可抑制由构造函数定义的隐式转换。explicit关键字只能用于类内部的构造函数声明上,在类的定义体外部所做的定义上不再重复它。
通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为explicit。将构造函数设置为explicit可以避免错误,并且当转换有用时,用户可以显示地构造对象。
177.类成员的显式初始化要求类的全体数据成员都是public。
178.友元可以是普通的非成员函数,或前面定义的其他类的成员函数,或是整个类。将一个类设为友元,友元类的所有成员函数都可以访问授予友元关系的那个类的非公有成员。当我们将成员函数声明为友元时,函数名必须用该函数所属的类名字加以限定。
179.当我们在类的外部定义static成员时,无须重复指定static保留字,该保留字只出现在类定义体内部的声明处。
180.static函数没有this指针
static数据成员必须在类定义体的外部定义(正好一次)。static成员不是通过类构造函数进行初始化,而是应该在定义时进行初始化。
static成员函数不能被声明为const,毕竟,将成员函数声明为const就是承诺不会修改该函数所属的对象。但static数据成员可以为const,并且可以在类的定义体中进行初始化。const static数据成员在类的定义体中初始化时,该数据成员仍必须在类的定义体之外定义(不必再指定初始值)。最后,static成员函数也不能被声明为虚函数。
181.static成员不是类对象的组成部分
static数据成员的类型可以是该成员所属的类类型;非static成员被限定声明为其自身类对象的指针或引用。
static数据成员可用作默认实参;非static数据成员不能用作默认实参,因为它的值不能独立于所属的对象而使用。
182.复制构造函数是一种特殊构造函数,具有单个形参,改形参(常用const修饰)是对该类类型的引用,当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数。当将该类型的对象传递给函数或从函数返回该类型的对象时,将隐式使用复制构造函数。
合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本。
183.不管类是否定义了自己的析构函数,编译器都自动执行类中非static数据成员的析构函数。
184.有一种特别常见的情况需要类定义自己的复制控制成员的:类具有指针成员。
185.为了防止复制,类必须显示声明其复制构造函数为private。
如果复制构造函数是私有的,将不允许用户代码复制该类类型的对象,编译器将拒绝任何进行复制的尝试。
然而,类的友元和成员仍可以进行复制。如果想要连友元和成员中的复制也禁止,就可以声明一个(private)复制构造函数但不对其定义,这样就禁止了任何复制类类型对象的尝试:用户代码中的复制尝试将在编译时标记为错误,而成员函数和友元中的复制尝试将在链接时导致错误。
186.当对象的引用或指针超出作用域时,不会运行析构函数。只有删除指向动态分配对象的指针或实际对象(而不是对象的引用)超出作用域时,才会运行析构函数。
187.析构函数与复制构造函数或赋值操作符之间的一个重要区别是,即使我们编写了自己的析构函数,合成析构函数仍然运行。
188.即使对象赋值给自己,赋值操作符的正确工作也非常重要。保证这个行为的通用方法是显示检查对自身的赋值。
赋值操作符通常要做复制构造函数和析构函数也要完成的工作。在这种情况下,通用工作应放在private实用函数中。
189.重载操作符必须具有至少一个类类型或枚举类型的操作数。
190.操作符的优先级、结合性或操作数数目不能改变。
作为类成员的重载函数,其形参看起来比操作数数目少1。作为成员函数的操作符有一个隐含的this形参,限定为第一个操作数。
191.不要重载具有内置含义的操作符。重载逗号、取地址、逻辑与逻辑或等操作符通常不是好做法。这些操作符具有有用的内置含义,如果我们定义了自己的版本,就不能再使用这些内置含义。
192.IO操作符重载必须为非成员函数。
一般而言,输出操作符应输出对象的内容,进行最小限度的格式化,它们不应改输出换行符,这些应该由用户来控制。
输入操作符必须处理错误和文件结束的可能性。
193.类赋值操作符必须是类的成员,以便编译器可以知道是否需要合成一个。
194.算术操作符通常产生一个新值,该值是两个操作数的计算结果,它不同于任一操作数且在一个局部变量中计算,返回对那个变量的引用是一个运行时错误。既定义了算术操作符又定义了相关复合操作符的类,一般应使用复合赋值实现算术操作符。
195.赋值必须返回对*this的引用。
一般而言,赋值操作符与复合赋值操作符应返回左操作数的引用。
196.下标操作符必须定义为类成员函数。
下标操作符能用作赋值的左右操作数,一般返回引用。类定义下标操作符时,一般需要定义两个版本:一个为非const成员并返回引用,另一个为const成员并返回const引用。
197.重载箭头操作符必须返回指向类类型的指针,或者返回定义了自己的箭头操作符的类类型对象。
198.为了与内置类型一致,前缀式操作符应返回被增量或减量对象的引用。
为了与内置操作符一致,后缀式操作符应返回旧值(即,尚未自增或自减的值),并且,应作为值返回,而不是返回引用。
199.函数对象的函数适配器:绑定器,求反器。
200.转换操作符是一种特殊的类成员函数。
它定义将类类型值转变为其他类型值的转换。转换操作符在类定义体内声明,在保留字operator之后跟着转换的目标类型:
operator type();这里的type表示内置类型名、类类型名或由类型别名所定义的名字。
201.转换函数必须是成员函数,不能指定返回类型,并且形参表必须为空。
转换函数一般不应该改变被转换的对象。因此,转换操作符通常应定义为const成员。
202.类类型转换之后不能再跟另一个类类型转换。如果需要多个类类型转换,则代码将出错。
即只能进行一次类类型转换,不能同时进行一次以上的转换。
203.如果两个转换操作符都可用在一个调用中,而且在转换函数之后存在标准转换,则根据该标准转换的类别选择最佳匹配。
204.避免二义性最好的方法是避免编写互相提供隐式转换的成对的类,同时保证最多只有一种途径将一个类型转换为另一类型。
205.如果重载集中的两个函数可以用同一转换函数匹配,则使用在转换之后或之前的标准转换序列的等级来确定哪个函数具有最佳匹配。
否则,如果可以使用不同转换操作,则认为这两个转换是一样好的匹配,不管可能需要或不需要的标准转换的等级如何。
只有两个转换序列使用同一转换操作时,才用类类型转换之后的标准转换序列作为选择标准。
206.如果类既定义了转换操作符又定义了重载操作符,容易产生二义性。下面几条经验规则会有所帮助:
(1)不要定义相互转换的类,即如果类Foo具有接受类Bar的对象的构造函数,不要再为类Bar定义到类型Foo的转换操作符。
(2)避免到内置算术类型的转换。具体而言,如果定义了到算术类型的转换,则
不要定义接受算术类型的操作符的重载版本。如果用户需要使用这些操作符,转换操作符将转换你所定义的类型的对象,然后可以使用内置操作符。
不要定义转换到一个以上算术类型的转换。让标准转换提供到其他算术类型的转换。
最简单的规则是:对于那些“明显正确”的,应避免定义转换函数并限制非显示构造函数。
207.既为算术类型提供转换函数,又为同一类类型提供重载操作符,可能会导致重载操作符和内置操作符之间的二义性。
208.用引用(或指针)调用的虚函数在运行时确定,被调用的函数是引用(或指针)所指对象的实际类型所定义的。
209.为了指明函数为虚函数,在其返回类型前面加上保留字virtual。除了构造函数以外,任意非static成员函数都可以是虚函数。保留字只在类内部的成员函数声明中出现,不能用在类定义外部出现的函数定义上。
210.基类通常应将派生类需要重定义的任意函数定义为虚函数。
只有通过引用或指针调用,虚函数才在运行时确定。
211.派生类中虚函数的声明必须与基类中的定义方式完全匹配,但有一个例外:返回对基类型的引用(或指针)的虚函数。
派生类中的虚函数可以返回基类函数所返回类型的派生类的引用(或指针)。
212.用作基类的类必须是已定义的。
213.如果需要声明(但并不实现)一个派生类,则声明包含类名但不包含派生列表。
214.只有成员函数中的代码才应该使用作用域操作符覆盖虚函数机制。
215.派生类虚函数调用基类版本时,必须显式使用作用域操作符。如果派生类函数忽略了这样做,则函数调用会在运行时确定并且将是一个自身调用,从而导致无穷递归。
216.如果一个虚函数的调用省略了具有默认值的实参,则所用的值由调用该函数的类型定义,与对象的动态类型无关。
217.每个类控制它所定义的成员的访问。派生类可以进一步限制但不能放松对所继承的成员的访问。
218.使用class保留字定义的派生类默认具有private继承,而用struct保留字定义的类默认具有public继承。
219.友元关系不能继承。基类的友元对派生类的成员没有特殊访问权限。如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。
220.要确定到基类的转换是否可访问,可以考虑基类的public成员是否可访问,如果可以,转换是可访问的,否则,转换是不可访问的。
221.构造函数和复制控制成员不能继承,每个类定义自己的构造函数和复制控制成员。
派生类的构造函数受继承关系的影响,每个派生类构造函数除了初始化自己的数据成员之外,还要初始化基类。
222.派生类只能初始化直接基类。
223.派生类析构函数不负责撤销基类对象的成员。编译器总是显示调用派生类对象基类部分的析构函数。每个析构函数只负责清除自己的成员。
224.如果在构造函数或析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本。
无论由构造函数或析构函数直接调用虚函数,或者从构造函数或析构函数所调用的函数间接调用虚函数,都应用这种绑定。
225.在继承情况下,派生类的作用于嵌套在基类作用域中。
226.局部作用域中声明的函数不会重载全局作用域中定义的函数,同样,派生类中定义的函数也不重载基类中定义的成员。通过派生类对象调用函数时,实参必须与派生类中定义的版本相匹配,只有在派生类根本没有定义该函数时,才考虑基类函数。
227.将函数定义为纯虚能够说明,该函数为后代类型提供了可以覆盖的接口,但是这个类中的版本决不会调用。
含有(或继承)一个或多个纯虚函数的类是抽象基类。除了作为抽象基类的派生类的对象的组成部分,不能创建抽象类型的对象。
228.类型形参由关键字class或typename后接说明符构成。用作模板形参的名字不能在模板内部重用。这一限制还意味着模板形参的名字只能在同一模板形参表中使用一次。
229.通过在成员名前加上关键字typename作为前缀,可以告诉编译器将成员当作类型。
230.在函数模板内部完成的操作限制了可用于实例化该函数的类型。
程序员的责任是,保证用作函数实参的类型实际上支持所用的任意操作,以及保证在模板使用那些操作的环境中那些操作运行正常。
编写模板代码时,对实参类型的要求尽可能少是很有益的。
231.模板实参推断:
多个类型形参的实参必须完全匹配
类型形参的实参的受限转换:
const转换:接受const引用或const指针的函数可以分别用非const对象的引用或指针来调用,无须产生新的实例化。如果函数接受非引用类型,形参类型和实参都忽略const,即,无论传递const或非const对象给接受非引用类型的函数,都使用相同的实例化。
数组或函数到指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指针转换。数组实参将当作指向其第一个元素的指针,函数实参当作指向函数类型的指针。
232.用模板形参定义的函数形参的实参允许进行常规转换
233.类模板的成员函数只有为程序所用才进行实例化。如果某函数从未使用,则不会实例化该成员函数。
234.类模板的指针定义不会对类进行实例化,只有用到这样的指针时才会对类进行实例化。因此指针成员变量在类对象创建时不会实例化,而是只有在对其进行操作时,才会实例化。
235.非类型模板实参必须是编译时常量表达式。
236.声明依赖性:当授予对给定模板的所有实例的访问权的时候,在作用域中不需要存在该类模板或函数模板的声明。实质上,编译器将友元声明也当作类或函数的声明对待。但如果没有事先告诉编译器该友元是一个模板,则编译器将认为该友元是一个普通非模板类或非模板函数。所以,要么直接以模板形参的方式声明友元,要么提前声明使用的类或函数是一个模板。
237.任意类(模板或非模板)可以拥有本身为类模板或函数模板的成员,这种成员称为成员模板,成员模板不能为虚。
238.模板特化是这样的一个定义,该定义中一个或多个模板形参的实际类型或实际值是指定的。
239.对具有同一模板实参集的同一模板,程序不能既有显示特化又有实例化。
240.在类特化外部定义成员时,成员之前不能加template<>标记。但在特化成员而不特化类时,定义特化成员时,则需要加上template<>标记。
241.部分特化的定义与通用模板的定义完全不会冲突。部分特化可以具有与通用类模板完全不同的成员集合。类模板成员的通用定义永远不会用来实例化类模板部分特化的成员。
242.当匹配同样好时,非模板版本优先。
243.export 用来指出编译器必须记住相关模板定义位置的关键字,供支持模板实例化的分别编译模型的编译器使用。在一个程序中一个模板只能用export关键字定义一次。
244.包含编译模型,编译器用来寻找模板定义的机制,它依赖于模板定义被包含在每个使用模板的文件中。一般而言,模板定义存储在一个头文件中,使用模板的任意文件必须包含该头文件。
245.在使用任意实参特化的模板之前,必须先出现模板特化。
246.类模板的类型实参必须在使用类的时候指定。
247.处理异常栈展开期间,释放局部对象所用的内存并运行类类型局部对象的析构函数。
248.析构函数应该从不抛出异常。
249.除下面几种可能的区别之外,异常的类型与catch说明符的类型必须完全匹配额:
允许从非const到const的转换;允许从派生类型到基类类型的转换;将数组/函数转换为对应的指针类型。
250.通常,如果catch子句处理因继承而相关的类型的异常,它就应该将自己的形参定义为引用。
251.因为catch子句按出现次序匹配,所以使用来自继承层次的异常的程序必须将它们的catch子句排序,以便派生类型的处理代码出现在其基类类型的catch之前。
带有因继承而相关的类型的多个catch子句,必须从最低派生类型到最高派生类型排序。
252.如果catch(...)与其他catch子句结合使用,它必须是最后一个,否则,任何跟在它后面的catch子句都将不能被匹配。
253.构造函数要处理来自构造函数初始化式的异常,唯一的方法是将构造函数编写为函数测试块。
254.auto_ptr对象只能保存一个指向对象的指针,并且不能用于指向动态分配的数组,使用auto_ptr对象指向动态分配的数组会导致未定义的运行时行为。它使用的普通的delete操作符,而不用数组的delete[]操作符。
auto_ptr的主要目的是,在保证自动删除auto_ptr对象引用的对象的同时,支持普通指针式行为。
255.auto_ptr对象的复制和赋值是破坏性操作,不能将其存储在标准容器中:与其他复制或赋值操作不同,auto_ptr的复制和赋值改变右操作数,因此,赋值的左右操作数必须都是可修改的左值。
256.不要使用auto_ptr对象保存指向静态非配对象的指针,否则当其准备被撤销的时候,将试图删除指向非动态分配对象的指针。
257.永远不要使用两个auto_ptr对象指向同一对象。
应该只用get询问auto_ptr对象或者使用返回的指针值,不能用get作为创建其他auto_ptr对象的实参。
258.如果一个函数声明没有指定异常说明,则该函数可以抛出任意类型的异常。
如果函数抛出了没有在其异常说明中列出的异常,就调用unexpected。默认情况下,unexpected函数调用terminate函数,terminate函数一般会终止程序。
259.基类中的异常列表是虚函数的派生类版本可以抛出的异常列表的超集。
260.命名空间为防止名字冲突提供了更加可控的机制,命名空间能够划分全局命名空间,这样使用独立开发的库就更加容易了。
命名空间可以在全局作用域或其他作用域内部定义,但不能在函数或类内部定义。
261.命名空间可以是不连续的,与其他作用域不同,命名空间可以在几个部分中定义。命名空间由它的分离定义部分的总和构成,命名空间是累积的。
未命名的命名空间可以在给定文件中不连续,但不能跨越文件,每个文件有自己的未命名的命名空间。
262.不能在不相关的命名空间中定义成员。
263.如果函数具有类类型形参就使得函数可见,其原因在于,允许无须单独的using声明就可以使用概念上作为类接口组成部分的非成员函数。
接受类类型形参(或类类型指针及引用形参)的函数(包括重载操作符),以及与类本身定义在同一命名空间中的函数(包括重载操作符),在用类类型对象(或类类型的引用及指针)作为实参的时候是可见的。
264.为了提供命名空间中所定义模板的自己的特化,必须保证在包含原始模板定义的命名空间中定义特化。
265.如果派生类定义了自己的复制构造函数或赋值操作符,则类负责复制(赋值)所有的基类子部分,而不自动复制或赋值基类部分。
266.虚继承是一种机制,类通过虚继承指出它希望共享其虚基类的状态。在虚继承下,对给定虚基类,无论该类在派生层次中作为虚基类出现多少次,只继承一个共享的基类子对象。共享的基类子对象称为虚基类。
由最低层派生类的构造函数初始化虚基类,无论虚基类出现在继承层次中任何地方,总是在构造非虚基类之前构造虚基类。
267.内存分配与对象构造分离
allocator类/标准库中的operator new和operator delete
new:1.调用名为operator new的标准库函数,分配足够大的原始的未类型化的内存,以保存指定类型的一个对象;2.运行该类型的一个构造函数,用指定初始化式构造对象;3.返回指向新分配并构造的对象的指针。
delete:1.对指针指向的对象运行适当的析构函数;2.通过调用名为operator delete的标准库函数释放该对象所用内存。
它们均是在void*指针而不是类型化的指针上进行操作;allocator类则不同。
268.定位new表达式使我们能够在特定的、预分配的内存地址构造一个对象。
对值型类而言,在适当的位置直接构造对象与构造临时对象并进行复制之间没有可观察到的区别,而且性能差别基本没有意义。但对某些类而言,使用复制构造函数是不可能的(因为复制构造函数是私有的),或者是应该避免的,在这种情况下,也许有必要使用定位new表达式。
269.显示调用析构函数的效果是适当地清除对象本身。但是,并没有释放对象所占的内存,如果需要,可以重用该内存空间。
调用operator delete函数不会运行析构函数,它只释放指定的内存。
270.编译器看到类类型的new或delete表达式的时候,它查看该类是否有operator new或operator delete成员,如果类定义(或继承)了自己的成员new和delete函数,则使用那些函数为对象分配和释放内存;否则,调用这些函数的标准库版本。
271.成员new和delete函数必须是静态的(隐式为静态函数,不必显示声明),因为它们要么在构造对象之前使用(operator new),要么在撤销对象之后使用(operator delete),因此,这些函数没有成员数据可操纵。
272.通过运行时类型识别(RTTI),程序能够使用基类的指针或引用来检索这些指针或引用所指对象的实际派生类型。
273.只有当typeid的操作数是带虚函数的类类型的对象的时候,才返回动态类型信号。测试指针(相对于指针指向的对象)返回指针的静态的、编译时类型。
274.默认构造函数和复制构造函数以及赋值操作符都定义为private,所以不能定义或复制type_info类型的对象。程序中创建type_info对象的唯一方法是使用typeid操作符。
275.typeinfo对name返回值的唯一保证是,它为每个类型返回唯一的字符串。
276.调用操作符的优先级高于成员指针操作符。
277.嵌套类的名字在其外围类的作用域中可见,但在其他类作用域或定义外围类的作用域中不可见。嵌套类的名字将不会与另一作用域中声明的名字冲突。
278.在其类外部定义的嵌套类成员,必须定义在定义外围类的同一作用域中。在其类外部定义的嵌套类的成员,不能定义在外围类内部,嵌套类的成员不是外围类的成员。
外围作用域的对象与其嵌套类型的对象之间没有联系。
279.每个union对象的大小在编译时是固定的:它至少与union的最大数据成员一样大。
union也可以定义成员函数,包括构造函数和析构函数。但是,union不能作为基类使用,所以成员函数不能为虚数。
union不能具有静态数据成员或引用成员,而且,union不能具有定义了构造函数、析构函数或赋值操作符的类类型的成员。
不能用于定义对象的未命名union称为匿名联合。
匿名union不能有私有成员或受保护成员,也不能定义成员函数。
280.局部类的所有成员(包括函数)必须完全定义在类定义体内部,因此,局部类远不如嵌套类有用。
局部类不能使用函数作用域中的变量,它们可以使用外围函数中定义的类型名、静态变量和枚举成员。
局部类不能有静态成员。
281.地址操作符(&)不能应用于位域,所以不可能有引用类位域的指针,位域也不能是类的静态成员。
282.对待const和volatile的一个重要区别是,不能使用合成的复制和赋值操作符从volatile对象进行初始化或赋值。合成的复制控制成员接受const形参,这些形参是对类类型的const引用,但是,不能将volatile对象传递给普通引用或const引用。
虽然可以定义复制控制成员来处理volatile对象,但更深入的问题是复制volatile对象是否有意义,对该问题的回答与在任意特定程序中使用volatile的原因密切相关。
283.C函数的指针与C++函数的指针具有不同的类型,不能将C函数的指针初始化或赋值为C++函数的指针(反之亦然)。
284.类的自定义内存管理有两种方式:定义自己的内部内存分配,以简化自己的数据成员的分配;定义自己的、类特定的operator new和operator delete函数,在分配类类型的新对象时使用它们。
285.运行时类型识别RTTI只适用于定义了虚函数的类,没有定义虚函数的类型的类型信息是可用的但反映静态类型。
286.volatile 告诉编译器不要执行某些优化的信号
287.嵌套类:它是在另一类的作用域中定义的类,这样的类经常定义为其外围类的具体实现类。
联合:是只能包含简单数据成员的一种特殊类。union类型的对象在任意时刻只能为它的一个数据成员定义值。联合经常嵌套在其他类类型内部。
局部类:是局部于函数而定义的非常简单的类。局部类的所有成员必须定义在类定义体中,局部类没有静态数据成员。
288.C++支持几种固有的不可移植的特征,包括位域和volatile(它们可使与硬件接口更容易)以及链接指示(它使得与用其他语言编写的程序接口更容易)。