C++ primer 学习笔记(六)

第十章 关联容器

1.关联容器支持通过键来高效地查找和读取元素;

2.关联容器不提供front、push_front、pop_front、back、push_back以及pop_back操作;

3.要使用map对象,则必须包含map头文件;

4.所有的比较函数必须在键类型上定义严格弱排序;

5.在实际应用中,键类型必须定义<操作符,而且该操作符应能“正确地工作”;

6.使用下标访问map中不存在的元素将导致在map容器中添加一个新的元素,它的键即为该下标值;

7.map容器和set容器一样,存储的键必须唯一,而且不能修改(键的类型均为const);

8.multimap不支持下标运算;

 

 

扫描二维码关注公众号,回复: 6545328 查看本文章

第十一章 泛型算法

1.使用泛型算法必须包含algorithm头文件;

2.标准库还定义了一组泛化的算术算法,其命名习惯与泛型算法相同,使用这些算法必须包含numeric头文件;

3.使用插入迭代器back_inserter的程序必须包含iterator头文件;

4.通常,如果要以一个已存在的容器为副本创建新容器,更好的方法是直接用输入范围作为性构造容器的初始化式;

5.算法不直接修改容器的大小,如果需要添加和删除元素,则必须使用容器操作;

6.谓词是做某些检测的函数,返回用于条件判断的类型,指出条件是否成立;

7.流迭代器值定义了最基本的迭代器操作:自增、解引用和赋值。此外,可比较两个istream迭代器是否相等;而ostream迭代器则不通过比较运算;

8.流迭代器不能创建反向迭代器;

9.需要低级类别迭代器的地方,可以使用任意一种更高级的迭代器;

 

 

 

 

 

 

 

 

第三部分类和数据抽象

 

第十二章 类

1.将关键字const家在形参表之后,就可以将成员函数声明为常量:

double avg_price()const;

const成员不能改变其所操作对象的数据成员,const必须同时出现在声明和定义中,若只出现在其中一处,就会出现一个编译时错误;

  1. 类背后蕴涵的基本思想是数据抽象和封装;

只有当某一个类定义已经在前面出现,数据类型才能别被的类所用,一般相互包含的类,需要进行前向声明。定义类类型时,不分配真实的内存空间,当类类型定义完成,定义类的对象时,才会分配内存空间

成员函数有个附加的隐含形参,即指向该类对象的一个指针this;

3.在const成员函数中,this的类型是一个指向const类类型对象的const指针。既不能改变this所指向的对象,也不能改变this所保存的地址;

4.不能从const成员函数返回指向类对象的普通引用。Const成员函数只能返回*this作为一个const引用;

5.可以通过声明为mutable来实现对类的数据成员的修改(甚至在const成员函数内),将关键字mutable放在成员声明之前:

mutable size_t access_ctr;

  1. 可变数据成员永远都不能为const,甚至当它是const对象的成员时也如此,因此const成员函数可以改变mutable成员;

构造函数不能设置为const,构造函数完成的是对对象的初始化,无论是const对象还是非const对象。

构造函数与其他成员函数不同的是可以拥有初始化参数列表。

必须对任何const或者引用类型成员,以及没有默认构造函数的类类型使用参数初始化列表。参数初始化的顺序和类中声明的顺序一致,和初始化列表顺序无关,建议初始化列表的参数初始化顺序和声明的顺序一致。

7.即使两个类具有完全相同的成员列表,它们也是不同的类型。每个类的成员不同于任何其他类(或任何其他作用域)的成员;

8.在类作用域之外,成员只能通过对象或指针分别使用成员访问操作符 . 或->来访问;

9.出现在类的定义体之外的成员定义必须指明成员出现在那个类中:

double Sales_item::avg_price()const

{……}

10.在定义于类外部的成员函数中,形参表和成员函数体出现在成员名之后,这些都是在类作用域中定义,所以可以不用限定而引用其他成员;

11.返回类型出现在成员名字前面,如果函数在类定义体之外定义,则用于返回类型的名字在类作用域之外,如果返回类型使用由类定义的类型,则必须使用完全限定名;

只有没有定义构造函数,而且全体数据成员都为public的类,才可以采用初始化数组相同的方式——显示初始化成员,而且初始化成员的顺序必须和public中定义的顺序相同。

12.类定义实际上式在两个阶段中处理:

首先,编译成员声明;只有在所有成员出现之后,才编译它们的定义本身;

13.  类成员定义中的查找:

首先检查成员函数局部作用域中的声明;

如果在成员函数中找不到该名字的声明,则检查对所有类成员的声明;

如果在类中找不到该名字的声明,则检查在此成员函数定义之前的作用域中出现的声明;

14.使用相同名字来表示形参和成员,可能导致类的成员被屏蔽,但是仍然可以通过用类名来限定成员名或显式使用this指针使用它;

15.构造函数初始化式只在构造函数的定义中而不是声明中指定;

16.省略初始化列表并在构造函数的函数体内对数据成员赋值是合法的;

17.没有默认构造函数的类类型成员,以及const或引用类型的成员,都必须在构造函数初始化列表中进行初始化;

18.成员被初始化的次序就是定义成员的次序;

19.内置和复合类型的成员,如指针和数组,只对定义在全局作用域中的对象初始化;

20.使用默认构造函数定义一个对象的正确方式是去掉最后的空括号:

sales_item myobj(); //error ,defines a function, not an object

sales_item myob ; // ok

21.可以通过将构造函数声明为explicit,来防止在需要隐式转换的上下文中使用构造函数;

22.explicit关键字只能用于类内部的构造函数声明上,在类的定义体外部所做的定义上不再重复它;

23.友元可以使普通的非成员函数,或前面定义的其他类的成员函数,或整个类。将一个类设为友元,友元类的所有成员函数后可以访问授予友元关系的那个类的非公有成员;

24.类必须将重载函数集中每一个希望设为友元的函数都声明为友元;

25.可以通过作用域操作符从类直接调用static成员,或者通过对象、引用或指向该类类型对象的指针间接调用;

26.类成员函数可以不用作用域操作符来引用类的static成员;

 

 

第十三章 复制控制

1. 复制构造函数具有单个形参,该形参(常用const修饰)是对该类类型的引用,当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数,当将该类型的对象传递给函数或从函数返回该类型的对象时,将隐式使用复制构造函数;

如果需要禁止复制,则可以采用在private中显示的声明复制构造函数。

2.不管类是否定义了自己的析构函数,编译器都自动执行类中非static数据成员的析构函数;

3. 不能对IO类型的对象使用复制初始化;

4.当形参或返回值为类类型时,有复制构造函数进行赋值;

5.即使定义了其他构造函数,也会合成赋值构造函数,合成复制构造函数的行为时,执行逐个成员初始化,将新对象初始化为原对象的副本;

6.只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义复制构造函数,也可以复制;

7.为了防止赋值,类必须显式声明其复制构造函数为private,然而,类的友元和成员仍可进行赋值,如果想要连友元和成员中的复制也禁止,就可以声明一个private复制构造函数但不对其定义;

8.一般来说,最好显式或隐式定义默认构造函数和复制构造函数。只有不存在其他构造函数时才合成默认构造函数。如果定义了复制构造函数,也必须定义默认构造函数;

9.如果类需要析构函数,则它也需要赋值操作符和复制构造函数;

10.析构函数没有返回值,没有形参,也不能进行重载;

11.编写自己的复制构造函数时,必须显式复制需要复制的任意成员。显式定义的复制构造函数不会进行任何自动复制;

 

 

  

 

 

 

  • 重载操作符与转换

  1. 不能通过连接其他合法符号来创建任何新的操作符;用于内置类型的操作符,其含义不能改变,也不能为任何内置类型定义额外的新的操作符;重载操作符,必须具有一个类类型操作数

3.操作符的优先级、结合性或操作数数目不能改变;

 

4.重载逻辑操作符不再具备短路求值的特性;

5.输入操作符必须处理错误和文件结束的可能性;

6.一般而言,赋值操作符与复合赋值操作符应返回左操作数的引用;

7.普通重载不能区别所定义的是前缀式操作符,为了解决这个问题,后缀式操作符函数接受一个额外的(即,无用的)int形参,使用后缀式操作符时,编译器提供0作为这个形参的实参;

CheckedPtr& CheckedPtr::operator--();   //prefix operators

CheckedPtr& CheckedPtr::operator--(int); //postfix operators

 

8.可以为类类型的对象重载函数调用操作符,一般为表示操作的类重载调用操作符;

struct absint{

int operator()(int val){

   return val<0 ?-val :val;

}

   };

 

9. 标准库提供了一组函数适配器,分为绑定器(bind1st 和bind2nd)和求反器(not1和not2):

 bind1st将给定值绑定到二元函数对象的第一个实参,bind2nd将给定值绑定到二元函数对象的第二个实参;

 not1将一元函数对象的真值求反,not2将二元函数的对象的真值求反;

10.转换操作符定义将类类型值转变为其他类型值的转换,转换操作符在类定义体内声明,必须是成员函数,并且形参表必须为空。采用如下通用形式:

operator type();

这里type表示内置类型名、类类型名或由类型别名所定义的名字。对任何可作为函数返回类型的类型(除了void之外)都可以定义转换函数。一般而言,不允许转换为数组或函数类型,转换为指针类型(数据和函数指针)以及引用类型是可以的;

11.虽然转换函数不能指定返回类型,但是每个转换函数必须显式返回一个指定类型的值;

12.转换函数一般不应该改变被转换对象,因此转换操作符通常应定义为const成员;

13.类类型转换之后不能再跟另一个类类型转换,如果需要多个类类型转换,则代码将出错;

14.既为算术类型提供转换函数,又为同一类类型提供重载操作符,可能会导致重载操作符和内置操作符之间的二义性;

 

 

 

 

 

 

 

 

第四部分面向对象编程与泛型编程

 

第十五章 面向对象编程

1.       在C++中,多态性仅用于通过继承而相关联的类型的引用和指针;

2.       除了构造函数以外,任意非static成员函数都可以是虚函数。保留字virtual只在类内部的成员函数声明中出现,不能用在类定义体外部出现的函数定义上;

3.       protected成员不能被类的用户访问,但可被类的派生类访问;

4.       派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊访问权限;

5.       派生类的虚函数可以返回基类函数所返回类型的派生类的引用或指针;

6.       一旦函数在基类中声明为虚函数,它就一直为虚函数,派生类无法改变该函数为虚函数的这一事实;

7.       派生类对象由多个部分组成:派生类本身定义的(非static)成员加上由基类(非static)成员组成的子对象;

8.       C++语言不要求编译器将对象的基类部分和派生部分连续排列;

9.       用作基类的类必须是已定义的的;

10.   继承的分类:

如果是公用继承,基类成员保持自己的访问级别:基类的public成员为派生类的public成员,基类的protected成员为派生类的protected成员;

如果是受保护继承,基类的public和protected成员在派生类中为protected成员;

如果是私有继承,基类的所有成员在派生类中为private成员;

11.   struct定义的类和class定义的类唯一的不同知识默认成员的保护级别和默认的派生保护级别,struct默认的具有public继承,而class则具有private继承;

12.   友元可以访问类的private和protected数据;

13.   友元关系不能继承,基类友元关系对派生类的成员没有特殊访问权限,如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类;

14.   一般可以使用派生类型的对象对基类对象进行赋值或初始化;

15.   对基类对象进行赋值和初始化,实际上是在调用函数:初始化时定义构造函数,赋值时调用赋值操作符;

16.   如果是public继承,则用户代码和后代类都可以使用派生类到基类的转换。如果类是使用private或protected继承派生的,则用户代码不能将派生类对象转换为基类对象。如果是private继承,则从private继承类派生的类不能转换为基类。如果是protected继承,则后续派生类的成员可以转换为基类类型;

17.   基类对象只能是基类对象,它不能包含派生类型的对象;

18.   当构造、复制、赋值和撤销派生类型对象时,也会构造、复制、赋值和撤销这些基类子对象;

19.   派生类的合成默认构造函数除了初始化派生类的数据成员之外,它还初始化派生类对象的基类部分;

20.   派生类构造函数通过将基类包含在构造函数初始化列表中来间接初始化继承成员;

21.   派生类析构函数不负责撤销基类对象的成员;

22.   与基类成员同名的派生类成员将屏蔽对基类成员的直接访问,但仍可使用作用域操作符访问被屏蔽的基类成员;

23.   在基类和派生类中使用同一个名字的成员函数,其行为与数据成员一样:在派生类作用域中派生类成员将屏蔽基类成员。即使函数原型不同,基类成员也会被屏蔽;

24.   局部作用域中声明的函数不会重载全局作用域中定义的函数,同样,派生类中定义的函数也不能重载基类中定义的成员。通过派生类对象调用函数时,实参必须与派生类中定义的版本相匹配,只有在派生类根本没有定义该函数时,才考虑基类函数;

25.   含有或继承一个或多个纯虚函数的类是抽象基类。除了作为抽象基类的派生类的对象的组成部分,不能创建抽象类型的对象;

 

第十六章 模板与泛型编程

1.       模板定义以关键字template开始,后接模板形参表,模板形参表使用尖括号括住的一个或多个模板形参的列表,形参之间以逗号分隔;

2.       模板形参表不能为空;

3.       模板形参可以是表示类型的类型形参,也可以是表示常量表达式的非类型形参;

4.       使用函数模板时,编译器会推断哪个(或哪些)模板实参绑定到模板形参。一旦编译器确定了实际的模板实参,就称它实例化了函数模板的一个实例;

5.       与全局作用域中声明的对象、函数或类型同名的模板形参会屏蔽全局名字;

6.       用作模板形参的名字不能在模板内重用,模板形参的名字只能在同一模板形参表中使用一次;

7.       模板形参的名字可以在不同模板中重用;

8.       每个模板类型形参前面必须带上关键字class或typename,每个非类型形参前面必须带上类型名字,省略关键字或类型说明符是错误的;

9.       编写泛型代码的两个重要原则:

模板的形参是const引用;

函数体中的测试只用<比较;

10.   模板编译模型分为包含编译模型和分别编译模型两种;

11.   export关键字能够指明给定的定义可能会需要在其他文件中产生实例化;

12.   类模板成员函数的定义具有如下形式:

必须以关键字template开头,后接类的模板形参表;

必须指出它是哪个类的成员;

类名必须包含其模板形参;

   example:template <class T> ret-type(返回类型)Queue<T> ::member-name;

13.   在实例化类模板成员函数的时候,编译器不执行模板形参推断,相反,类模板成员函数的模板形参由调用该函数的对象的类型确定;

14.   类模板成员函数只有为程序所用才进行实例化,如果某函数从未使用,则不会实例化该成员函数;

15.   非类型模板实参必须是编译时常量表达式;

16.   在类模板中可以出现三种友元声明:

普通非模板类或函数的友元声明,将友元关系授予明确指定的类或函数;

类模板或函数模板的友元声明,授予对友元所有实例的访问权;

只授予对类模板或函数模板的特定实例的访问权的友元声明;

17.   模板特化:一个或多个模板形参的实际类型或实际值是指定的,特化的形式如下:

关键字template后面接一对空的尖括号(< >);

再接模板名和一对尖括号,尖括号中指定这个特化定义的模板形参;

函数形参表和函数体;

18.   对具有同一模板实参集的同一模板,程序不能既有显式特化又有实例化;

19.   特化出现在对该模板实力的调用之后是错误的;

猜你喜欢

转载自blog.csdn.net/qq_34935373/article/details/90897486
今日推荐