C++笔记

1  标准IO库对象不可复制和赋值,所以IO库对象不能作为存储在vector(或其他)容器中,并且形参或者返回值不能为流对象,如果需要传参或返回IO对象时,必须传递或返回对象的指针或引用。

2  标准IO的条件状态类型为strm::iostate(其中strm对象的是流对象,比如fstream),包括strm::badbit(破坏的流对象)、strm::failbit(失败的IO操作)、strm::eofbit(文件结束符),分别对应的判断函数为s.eof()、s.fail()、s.bad(),当3者都为true时,s.god()为true,清楚状态有s.clear()(清除所有)、s.clear(flag)(清除某状态)、s.setstate(flag)(设定状态)、s.rdstate()(返回当前的条件状态)。

3 输出缓存区的刷新可以通过一些操纵符,比如std::endl、std::ends、std::flush,效果分别是插入新行、null、不插入任何字符,也可以通过std::unitbuf设置每次输出都刷新缓存去,可以用std::nounitbuf取消,还可以通过将输入和输出绑定在一起,通过std::tie函数实现,保证任何输出,包括给用户的提示,都在试图读之前输出。

4 当文件流创建时,指定了文件路径的话,在使用的时候,要注意判定是否为有效流对象。

5 可以把文件流对象重新指定一个新的文件绑定,在这之前需要先关闭上一个文件的绑定,如果在操作上一个文件中,改变了流状态的话,需要清除流对象的状态。

6 文件流模式有in、out、app、ate、trunc、binary,其中app是写文件之前到文件尾,ate表示打开文件后直接定位到文件尾,trunc表示打开文件时清空已存在的文件流,binary表示以二进制模式进行IO操作,文件流通过位运行来组合模式,注意组合模式的有效性。

7 文件模式是文件的属性而不是文件流的属性,每次绑定一个新的文件时候,文件模式都要设定(存在默认的文件模式,通过指定用什么文件流操作)。

8 字符串流对象的操作方式和文件流类似,一般提供输入输出的格式转换,特别地,当输入一个字符串结束时,CTRL+Z等价于文件结束标志,所以通常用这个表示输入结束。

 

 

9 顺序容器有3种,std::vector支持快速随机访问、std::list支持快速插入删除、std::deque双端队列,顺序容器适配器有std::stack栈、std::queue队列、std::priority_queue优先级队列。

10 容器内元素的类型约束:元素类型必须支持赋值运算、元素类型的对象必须可以复制。那么标准IO库对象不满足,auto_ptr不满足传统的复制和赋值,引用也不能作为容器的元素。

11 如果初始化容器时候,指定容器大小,那么元素类型必须具有默认构造函数(无参数的默认构造函数)。

12 容器决定了对应迭代器的运算,所有的迭代器都支持*iter、iter->mem、iter++、++iter、--iter、iter--、iter1 == iter2、iter1 != iter2,支持随机访问的容器(std::vector,std::deque)可以使用iter + n、iter - n、iter1 - iter2等。

13 迭代器的使用要注意s.begin()、s.end()、s.rbegin()、s.rend()的关系,其中s.end()表示最后一个元素的下一个,s.rend()表示第一个的前一个,好好理解这句话。

14 容器插入元素的操作中,要想一下操作对容器的性能影响,比如对于push_front,插入一个元素到容器前端,由于std::vector向量插入最前面时候,性能低下,所以向量不提供这个插入函数,所以要插入一个元素到向量前端必须使用insert函数。

15 插入元素或删除元素容易使迭代器失效,一定要注意迭代器的指向问题,即注意插入或者删除元素的返回值是什么。

16 不要存储end操作返回的迭代器,其实这个操作没有什么性能缺失,因为函数实现肯定inline的。

17 容器支持关系操作符,对应于字典顺序,当然,元素类型必须支持关系操作才行,否则则不能支持关系操作。

18 c.back()返回容器中最后一个元素的引用,c.front()返回第一个元素的引用。

19 删除元素和添加元素操作一样,要注意操作队容器性能的影响,pop_front,比如删除std::vector的第一个元素,性能低下,那么向量不提供这个删除函数,只能通过erase函数完成这个删除操作。

20 容器的赋值函数assign,如果使用一段迭代器返回的来完成这个操作,c.assign(b, e),那么b和e必须不是指向c的迭代器,因为assign的工作原理是先删除容器c的元素,然后将b和e返回的元素赋值给c容器。

21 容器的交换swap函数不删除任何元素,所以性能很好,由于未删除任何元素,那么迭代器任然有效。

22 选择何种容器完成操作时候,要综合考虑您的需求,容器都有自己易于处理和难以处理的操作,综合考虑才能选取性能最佳的,当无法权衡时候,请选择std::vector向量。

23 std::string类,就是一个字符容器,那么提供给容器的操作同样适用于std::string,也有一些只适用于std::string类型的操作,比如s.substr返回子字符串,s.append追加字符串,s.replace替换字符串和一些查找操作,如s.find,s.rfind,s.find_first_of,s.find_last_of,s.find_first_not_of,s.find_last_nof_of等。

24 std::string类的比较可以用==和!=来操作,如何需要比较std::string里面指定区间的比较,那么请使用s.compare函数,功能强大。

25 顺序容器适配器的基本容器都是使用顺序容器的,可以用默认的容器类型,比如std::stack默认是建立在deque上面的,std::queue建立在deque上面,std::priority_queue建立在vector上面,其中std::stack可以建立在3中顺序容器上面,std::queue需要提供push_front操作,那么只能建立在std::list和std::deque上面,std::priority_queue需要提供随机访问功能,那么只能建立在std::vector和std::deque上面,优先级队列需要元素类型提供<操作。适配器的初始化可以使用容器来初始化,但是要注意当前适配器的基本容器类型是什么,只能通过基本容器来完成适配器的初始化。

 

 

26 关联容器都是基于数据结构红黑树的实现,标准库提供std::map、std::set、std::multimap、std::multiset,map/multimap是用来存在键值对的关联数组,set/multiset是用来存放数据的集合,其中map和set中,map中的键唯一,set集合中的数据唯一,而multimap允许键不唯一,multiset允许数据不唯一。

27 std::map<k, v>中对应的PAIR类型为std::pair<const k, v>类型,即map中的键不允许修改,标准库默认使用键的<操作来实现键的比较,如果是自定义类的话,需要提供<的重载操作,并且键的比较函数需要严格的弱排序,即k1<k2,k2<k3那么k1必须小于k3。

28 使用下表访问map时候,map<string, int> word_count; word_count["Anna"] = 1;那么执行时候,先查找是否有键"Anna",如果找到,将键对应的值赋值为1,如果没有找到,那么它的值采用初始化,然后赋值为1,后添加到map中。

29 读取map中的元素,m.count(k)返回m中k出现的次数,m.find(k)返回元素对应的迭代器,如果不存在,返回超出末端的迭代器,其中m.count(k)返回0或者1,这样处理是为了使multimap和map具有相同的接口。

30 set和map提供的操作基本相同,multimap和multiset也类似,只不过提供了其他的一些解决方案,m.lower_bound(k)返回一个迭代器,返回键不小于k的第一个元素,m.upper_bound(k)返回一个迭代器,返回键大于k的第一个元素,m.equal_range(k)返回一个pair对象,其中分别存放lower_bound和upper_bound的迭代器。

 

 

31 泛型算法包含在algorithm头文件中,还有一组泛华的算术算法,包含在numeric头文件中。

32 标准库算法是建立在迭代器上的,C++提供了一些其他迭代器:插入迭代器、iostream迭代器、反向迭代器。

33 插入迭代器有back_inserter(创建使用push_back实现插入的迭代器),front_inserter(创建使用push_front实现的插入的迭代器),inserter(创建使用insert实现插入的迭代器),当然,使用时候,容器必须提供相应的操作才能使用对应的插入迭代器。

34 虽然iostream不是容器,但是标准库同样提供了iostream对象上面使用的迭代器,istream_iterator用于读取输入流,ostream_iterator用于写输出流。

35 迭代器的类型决定迭代器的操作,迭代器的操作决定可以使用的算法,标准库提供了5中迭代器:输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器。

 

 

36 类成员有3种:数据、函数、类型别名。

37 不能从const成员函数返回对象的普通引用,只能返回*this作为一个const引用。

28 如果希望在const成员函数中修改成员变量的值,需要将这些成员变量用mutable修饰。

29 构造函数不能申明为const。

30 可以初始化const对象和引用类型的对象,但是不能对他们赋值,所以当一个类中存在const对象或者引用时候需要注意。

31 但形参的构造函数应该为explicit,除非你有理由想要定义这个隐式转换,将构造函数设置为explicit可以避免错误,并且当转换有用时候,可以显示调用构造函数。

32 static成员函数不能申明为const,但是static成员变量可以用const修饰。

33 const static整形变量可以用来在类中初始化一个指定长度的数组,cosnt static数据成员在类的定义体重初始化时,该数据成员任必须在类的定义体之外进行定义。

34 static成员不是类对象的组成部分,那么可以在类中这么使用class A { public://.... private: static A sa; ...};。

35 为了防止赋值,类必须显示申明其赋值构造函数为private,当然大多数类应该提供复制构造函数和默认构造函数,赋值和复制构造函数常一起使用,实际上,应该将这两个操作符看做一个单元,如果需要其中一个,我们几乎也肯定需要另一个。

36 如果一个雷需要析构函数,则它应该也需要赋值操作操作符和赋值构造函数,这是一个有用的经验法则,这个规则称为三法则,指的是如果需要析构函数,则需要所有这三个赋值控制成员。

 

 

 

37 重载操作符必须具有一个类类型的操作数,比如以下这个就有问题int operator+(int,int)。

38 重载操作符不再具备短路求值特性,比如重载&&、||等时,就不具有短路求值特性,建议不要重载这几个操作符。

39 重载操作符可以通过重载为类的成员函数,这个形参看起来少一个,第一个为隐含的this形参,限定为第一个操作数。

40 不要重载具有内置含义的操作符,赋值操作符、取地址操作符和逗号操作符对类类型操作数有默认含义。

41 重载输入和输出操作符,一般都重载为类的友元函数,为了与IO标准库一致,操作符应该接受ostream&或istream&作为第一个形参,对象类型如果是重载输出操作符时,最好定义为const对象的引用,并返回对ostream或istream的引用。

42 重载输入操作符必须处理错误和文件结束的可能性。

43 一般而言,赋值操作符和复合赋值操作符应该返回做操作数的引用。

44 下标操作符必须定义为类成员函数,并且一般需要提供两个版本,一个为非const成员并返回引用,另一个为const成员并返回const引用。

45 为了与内置类型一致,前缀式操作符应该返回被增量或减量对象的引用,后缀式操作符应该返回旧值,并且作为值返回,而不是返回引用,后缀式Obj operator++(int)。

46 可以为类类型的对象重载函数调用操作符,即()操作符,这个是仿函数的实现原理,仿函数作用于标准算法库比函数更灵活,因为仿函数(类)具有状态。

47 标准库函数对象有一元函数对象(2个,一元减和逻辑非)和二元函数对象,函数对象可以通过函数适配器连接,用于特化和扩张一元和二元函数对象,函数适配器有绑定器和求反器,绑定期可以将一个操作数绑定到一个值到二元函数对象而把二元函数对象转换为一元函数对象,求反器将谓词函数对象的值求反。

48 可以重载转换操作符,转换函数必须是类的成员函数,不能指定返回值,并且形参表必须为空,转换函数一般不应该改变被转换的对象,因此,转换操作符通常被定义为const成员。

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

50 一般而言,给出一个类与多个内置类型的转换不是一种好的做法,这样很多地方会导致二义性。

51 当两个类定义了转换也容易产生二义性,要注意类的构造转换盒转换操作符之间会不会出现二义性。

 

 

52 任何非static成员函数都可以是虚函数,基类通常将派生类需要重新定义的任何函数定义为虚函数。

53 派生类可以重新设定重基类继承成员的访问级别。

54 重载赋值操作符时要注意防止自身赋值。

55 虚析构函数主要是防止基类的指针指向一个派生里的对象,析构这个指针的时候,派生类的对象没有执行析构函数。

56 赋值控制成员中,只有析构函数应该定义为虚函数,构造函数不能定义为虚函数,构造函数是在对象完全构造之前运行的,在析构函数运行的时候,对象的动态类型还不完整。

57 如果在构造函数和析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本,这个时候不会执行动态绑定。

58 在基类和派生类中使用同一名字的成员函数,其行为与数据成员一样,在派生类作用域中派生类成员将屏蔽基类成员,即使函数原形不同,基类成员也会被覆盖。

59 如果派生类重新定义了重载成员,则通过派生类型只能访问派生类中重新定义的那些成员,如果派生类想通过自身类型使用所有的重载版本,则派生类必须要么冲定义所有版本,要么一个也不重新定义。

60 虚函数必须在基类和派生类中用用同一原形,如果基类成员于派生类成员接受的实参不同,就没有办法通过基类类型的引用或指针调用派生类函数。

 

 

61 在模板定义内部指定类型,使用typename作为前缀就可以,比如希望编译器将size_type当做类型,那么可以显示告诉编译器这么做typename Parm::size_type* p;。

62 可以为模板指定费类型模板形参,比如template <typename T, size_t N>。

63 模板编译模型包括包含编译模型和分别编译模型。

64 类模板中的友元申明,可以是普通类型的友元,可以是一般模板的友元,还可以是特定模板的友元。

65 有时候泛型编程时有些特殊类型需要一些额外的处理或者处理与通用操作稍微不同,可以对类模板进行特化,可以特别成员而不特化类,也可以特化整个类,还可以部分特别类或者成员。

 

 

 

66 异常对象通过赋值被跑出表达式的结构创建,那么该结构必须是可以复制的类型。

67 抛出指针通常是一个坏主意,抛出指针要求在对应处理代码存在的任意地方存在指针所指向的对象。

68 如果一个快直接分配资源,并且在释放资源之前发生异常,那么在栈展开期间不会释放该资源。

69 析构函数应该从不抛出异常,如果在析构函数中抛出异常,那么在为某个异常进行栈展开的时候,析构函数如果又抛出自己的未处理的异常,那么将导致调用标准库terminate函数,这个函数调用abort函数终止程序。

70 构造函数内部所做的事情经常会抛出异常,在构造函数对象的时候发生异常,对象可能只是部分被构造,它的一些成员已经初始化,而另一些成员在异常发生之前还没有初始化,即使对象只是部分被构造了,也要保证将会适当的撤销已构造的成员。

71 不能不获取异常,如果有异常找不到匹配的,那么程序将调用库函数terminate函数。

72 异常说明符可以使用引用,如果不是引用,那么就将异常对象复制到catch形参中,catch操作异常对象的副本。

73 如果catch子句处理因继承而相关的类型的异常,它就应该将自己的形参定义为引用。

74 有可能单个catch不能完全处理一个异常,在进行了一些校正行动之后,catch可能确定该异常必须由调用链中的更上层的函数来处理,catch可以通过重新抛出将异常传递给函数调用链更上层的函数,重新抛出是后面不跟类型或表达式的throw,冲洗抛出不指定自己的异常,但任然将是一个异常对象沿链向上传递,被抛出的异常是原来的异常对象,而不是catch形参。

75 为了处理来自构造函数初始化的异常,必须将构造函数编写为函数测试块,构造函数处理来自函数初始化的异常,唯一的方法是将构造函数编写为函数测试块。

76 违反异常说明,就会调用标准库函数unexpected,默认情况下,该函数会调用terminate函数。

77 异常说明的一种重要情况是,如果函数可以保证不会抛出任何异常。

78 基类的虚函数的异常说明可以与派生类中对应虚函数的异常说明不同,但是派生类虚函数的异常说明必须与对应基类的异常说明同样严格,或者比后者更受限,派生类不能再异常说明列表中添加异常,原因在于,继承层次的用户应该能够编写依赖于该说明列表的代码。

79 函数指针的异常说明和上述情况类似。

 

80 auto_ptr只能用于管理从new返回的一个对象,不能用来管理动态分配的数组,因为auto_ptr析构的时候,会调用delete而不是delete[]来释放空间。

81 auto_ptr对象的复制和赋值是破坏性的操作,当赋值auto_ptr对象或者将它的值复制给其他auto_ptr对象的时候,将基本对象的所有权从原来的auto_ptr转移给了新的对象,原来的auto_ptr对象重置为未绑定状态。

82 应该只用get询问auto_ptr对象或者使用返回的指针值,不能用get作为创建其他auto_ptr对象的实参。

83 auto_ptr的reset函数,会删除对象先前绑定的对象,然后重置对象的绑定对象。

84 不要使用auto_ptr对象保存指向静态分配对象的指针。

 

 

85 通过运行时类型识别RTTI,程序能够使用基类的指针或引用来检索这些指针或引用所指对象的实际派生类型,通过typeid操作符和dynamic_cast操作符来实现,前者返回对象或引用所指向对象的实际类型,后者将基类类型的指针或引用安全地转换为派生类类型的指针或引用。

86 dynamic_cast转换指针时候,如果转换失败,那么返回NULL,dynamic_cast转换引用时,如果转换失败,那么将抛出bad_cast异常,因为没有空引用。

87 只有当typeid的操作数是带虚函数的类类型的对象的时候,才返回动态类型信息,测试指针返回指针的静态的、编译时类型。

88 type_info类可以返回类型信息的特定C风格的字符串。type_info类编译器而变,一些编译前提供附加的成员函数,哪些函数提供关于程序中所用类型的附加信息,可以通过编译器手册来理解type_info类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值