手记

某C++神作,就100句话而已

  • 假设p是指针,当delete p;时,后面一定要p=NULL将p指向空

  • cin cout cerr 都是iostream类型的对象。cout<<“hello world”<<endl; 其中cout是左操作数对象,<<是操作符,作用是将右操作数写到左操作数对象,"hello world"是右操作数,前面半句话的意思是将hello world写入cout对象。同理,<<endl是将endl写入cout,endl表示刷新缓冲区并换行。

  • std::cout ::是作用域操作符,表示std名空间下的cout,用来区别其它名空间同名变量。

  • 注释不能嵌套:/* /*…*/ */ 是错误的

  • 表示整数、字符和布尔值的算术类型合称为整形。区分类型是因为只有知道某地址值的类型,才能知道需要多少位表示这个变量和如何解释这些位。

  • 整形赋值超出取值范围,会被赋值数对该类型数值数目取模后的值。

  • long类型运行时代价远高于int类型。以0开头的字面值常量表示八进制、以0x开头的表示十六进制。任何字符可以表示为 \ooo【ooo表示一个八进制数】

  • 对象是内存中具有类型的区域。初始化不是赋值,初始化是创建并赋值。定义在函数体外的内置变量自动初始化成0,定义在函数体内的内置变量不进行自动初始化,类类型(string)调用默认构造函数初始化。

  • const把一个对象转换成一个常量【const int bufSize = 512】。 全局变量加const作用域只在本文件。再加extern可在整个程序访问。非const变量默认为extern。

  • 引用初始化后始终绑定到初始化的对象,不能改变【引用本身不占存储单元】。const对象必须由const引用指向【避免一个能修改,一个不能修改,既对象本身和代表其的引用必须同时为const】。普通引用不能初始化为常量,const引用可以。

  • typedef用来定义类型的同义词。

  • 头文件用于声明,不是定义,因而可以出现多次。定义的语句不应该放在头文件里,出现两次会导致多重定义链接错误。例外是头文件可以定义类,还有值在编译时就已经知道的const对象,还有inline函数。变量是声明还是定义看是否有extern,但不是绝对的,函数就看有无大括号{}

  • 两个迭代器指向同一个元素就相等【==】,否则不等。任何改变vector长度的操作都会使已存在的迭代器失效。difference_type存储迭代器间距离。

  • 设计良好的程序只有在强调速度时才在类实现的内部使用数组和指针。数组下标的正确类型是size_t。

  • 预处理器变量NULL在编译时会被数值0替换。指针做减法操作得到的是ptrdiff_t类型。指针p - 2 等效p[-2]。

  • 定义的多个变量具有相同的类型,就可以在for循环的初始化语句中同时定义他们。for(int a, b, c …)

  • C++强制要求指向const对象的指针也必须具有const特性,可以把非const对象地址赋给const指针。

  • const指针:int *const cur = &cuss; 指向int型变量的指针,并且本身不能修改。

  • C风格字符串:const char *cp = “some value”; strlen(用于计算c风格字符串字符个数,不包括串最后的’\0’)

  • 动态数组:int *p = new int[任意表达式,变量等] delete [] p;

  • new/delete是操作符,不但控制内存而且执行构造函数和析构函数,malloc/free是标准库函数,不在编译器控制权限内,不能执行构造和析构。

  • 使用数组初始化vector:int int_arr[arr_size] = {0,1,2,3}; vector ivec(int_arr, int_arr+arr_size);

  • int ival; int *pval; ival = pval = 0; 错误,pval = 0 返回的是指针,无法赋值给int。

  • ++i i++ 前置时返回自增后的值,后置时返回值后自增。前置性能好于后置。

  • 逗号表达式的结果是其最右边表达式的值。

  • if(ia[index++]<ia[index])这个表达式没有明确的计算顺序。除了 && || ?::外其他操作数没有规定计算顺序。不应该使用这种表达式,结果无法确定。

  • int val = 3.24+3;表达式先转换为高精度,再转换为左值类型。int *ip; ip=0;隐式转换0为空指针。

  • 包含signed和unsigned int的表达式会被转换成unsigned,如果为负数会有问题。

  • 数组名用做sizeof或取地址&的操作数时不被当做指针。sizeof计算栈中分配大小,静态变量在全局数据区不计算在内。

  • const_cast添加或删除const特性。

  • 空语句: ;

  • switch case break的判断表达式必须为整数值。case从匹配的开始执行直到结束,而不是只执行匹配的。

  • do{此处定义的变量循环条件中不可见}while(…)

  • throw 类型 每一个标准库异常类都有what()函数返回C风格用户输入的字符串。如果不存在catch语句,程序默认执行terminate的标准库函数。

  • 预处理器定义的调试用的常量:__FILE__ __LINE__ __TIME__ __DATE__【打印时间和日期很常用】

  • assert宏用来检查不可能发生的条件。

  • 函数实参如果和定义类型不一致,但可被转换成定义类型,那么也可以使用。

  • 如果需要保护指针指向的值,形参需要定义为指向const的指针。应将不修改实参的形参定义为const引用,好处是可以传递字符串字面值(和产生右值的表达式),非const引用反而不灵活。

  • 如果形参是引用,那么他只是实参的别名。int *&v v是引用,是某个指向int的指针的别名。

  • fun(int*) fun(int[]) fun(int[10])等价,但10其实没有任何用处。如果定义为fun(int (&arr)[10]),此时会检查参数是否有10个。

  • C风格字符串以NULL结尾是为了标识结束防止越界

  • 含有可变形参的函数:fun(parm_list, …)与省略符对应的实参暂停类型检查。

  • 返回值为viod的函数只能使用return;不能return expression。return;也只能用于返回值为void的函数。

  • 静态变量只在初次调用时初始化,static size_t ctr=0只执行一次。

  • 内联函数避免函数调用的开销:编译时展开为函数体中的表达式,免去函数调用的寄存器保存恢复、复制实参跳转等。

  • 内联函数定义在头文件。编译器将类内定义的成员函数当做内联函数。

  • 每个成员函数都有一个隐含的this指针。假设有成员函数bool same_isbn(…) const 最后的const改变了隐含的this形参的类型,这种成员函数叫常量成员函数,无法修改对象本身。const的对象,指针引用只能调用常量成员函数。

  • 没有前缀的成员都被假定为this在调用。

  • 默认构造函数按变量初始化规则初始化类中所有成员【内置类型作为局部变量时不初始化】。

  • 指向函数的指针:函数类型由返回值和形参决定,与函数名无关。函数名本身就是指针。fun==&fun Fcn pf=add pf(1,2)==(*pf)(1,2)

  • 函数的形参可以是指向函数的指针:FUN( bool(string&, string&) ) 或 FUN( bool (*)(string&, string&) )。FUN是函数名,FUN(这里都是形参)

  • iostream定义读写控制窗口的类;fstream定义读写已命名文件的类;sstream定义的类型用于读写内存中的string对象。前面加上w支持wchar_t类型。

  • 流的状态可以由bad,fail,eof,good函数获得,cin.fail()。

  • 每一个流都关联一个缓冲区,崩溃的程序不会自动刷新缓冲区,如果用输出调试程序,确保每次输出都flush或endl。tie函数可将istream和ostream绑一起,输入前首先会刷新输出。

  • fstream 既要定义对象又要捆绑文件【open或初始化时】。如果想用一个文件流对象读取多个文件,必须close()并clear()。所有流都可以用<<操作符。

  • 设置或清除多个二进制位状态:可以多次调用setstate,clear;可以用位或操作符在一次调用中传递多个状态的值。A|B生成了一个值,其对应于A和B的位都打开了,设置为1,其他都是0.

  • 引用不支持一般意义的赋值运算,没有容器的元素是引用。容器的容器< <之间必须有空格否则会被认为是<<移位操作符。

  • vector deque支持通过元素的位置实现随机访问,所以迭代器可以实现算术和关系运算。list容器的迭代器不支持算术运算(加减法iter+n,iter1+iter2),也不支持关系运算(<= < >= >【是元素的比较,类似于string】),只支持++ – == !=。

  • 由容器定义的类型:size_type iterator value_type reference…

  • list deque提供了push_front()。容器元素都是副本。insert push可能导致迭代器失效,当编写循环将元素插入到vector deque时,必须更新迭代器。size()返回个数,empty()返回布尔值。

  • 如果容器c为空,c.front() c.back()操作未定义!c[n]和c.at(n)只适用于vector deque,n<0或n>=c.size()操作未定义【c.at(n)会抛out_of_range】。

  • 容器操作函数find(begin, end, val) 返回值是迭代器,没找到返回end。

  • 容器类型和元素类型都相同,可以用赋值vec1=vec2。容器类型不同或元素类型不同,但是兼容可以用assign函数来赋值。

  • vector容器中的元素以连续的方式存放【动态数组】。有预先分配策略,需要重新分配时加倍当前容量。capacity函数获取目前能够存储的元素总数,reserve函数设置capacity。

  • string中的字符也是连续存储的,也有迭代器string::iterator。string类将string::npos定义为保证大于任何有效下标的值。

  • 本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。stack queue priority_queue

  • pair类型:pair<T1, T2> p1;p1.first p1.second 。makepair函数可以创建pair对象。vector<pair<int, int> >如果需要多个pair可以放在一个vector中。

  • set中元素不重复,相当于只有键没有值。map的函数大部分都有对应的。

  • 关联容器:容器元素根据键的次序排列。

  • map可以理解为关联数组,键就是下标。

  • map可以用迭代器遍历,按键排序。使用map的insert函数可以避免使用下标操作的副作用:不必要的初始化【如果key已经在map中则map保持不变,避免了初始化】

  • 带有pair形参的insert版本返回一个迭代器和一个bool值的pair对象。map.count(k)返回k出现的次数。map.find(k)若k存在返回迭代器。map的erase返回void。

  • multimap相同键对应的元素必定相邻存放。multimap.lower_bound(k)指向键不小于k的第一个元素,upper_bound(k)。equal_range(k)返回pair代表上下限。

  • 泛型算法:find函数基于迭代器,不同容器可使用相同find。算法从不添加和删除容器元素。it=find_first_of(it,…)可用于多种容器。

  • accumulate(…)累加 fill写入元素 back_insert插入迭代器能达到push_back的效果 copy replace_copy sort unique count_if 谓词是检测函数。

  • 流迭代器:访问特定类型的输入 istream_iterator cin_it(cin) 反向迭代器:reverse_iterator

  • const_iterator用于指定范围的迭代器必要类型一致。容器返回的迭代器是否const取决于容器元素是否const。

  • map set list提供的是双向迭代器。string vector deque提供的是随机访问迭代器【sort函数需要随机迭代器】。istream_iterator是输入迭代器,ostream_iterator是输出迭代器。

  • 对于list对象应该优先使用list容器特有的成员版本【能添加删除元素】,而不是泛型算法。

  • 简单说,类就是定义了一个新的类型和一个新的作用域。成员访问级别默认私有。在声明和定义处指定inline都是合法的。类的前向声明一般用来编写相互依赖的类。类定义以分号结束,之后可以跟对象列表。

  • mytye.func1().func2()想要这种形式,就必须返回*this的引用才能调用func2。成员函数是否为const等同于形参this是否const,所以可以重载。mutable用来声明数据成员可以修改【突破各种const的限制】。

  • 使用类外全局作用域的变量可以::var来获得。

  • 类中的const成员必须在初始化列表中初始化,无法再构造函数体内赋值。初始化const或引用类型或没有默认构造函数的类类型数据成员的唯一机会是构造函数的初始化列表【冒号开始,逗号分隔】。初始化顺序由定义顺序决定,而不是初始化列表顺序。

  • 友元:将非公有成员的访问权授予指定的类或函数。friend在类定义内部。友元引入的类名和函数(定义或声明)可以像预先声明的一样使用。

  • static成员函数没有this形参,可直接访问类的static成员,不能使用非static成员。

  • 静态数据成员属于一个类,而不属于类的各个对象。静态成员函数在所有对象建立之前或删除之后仍然使用。

  • 初始化时是否调用复制构造函数取决于是否有=【拷贝构造函数,复制也叫拷贝构造函数是用同一个类的一个对象初始化另一个对象,普通构造函数是用各种参数初始化一个类的对象】。构造函数如果是explicit必须严格按照定义使用构造函数,否则可以存在隐式转换。

  • 非引用形参将复制实参值,非引用return将复制return的东西。即使定义了其他构造函数,也会合成复制构造函数【能够复制类中的数组】。类成员有指针一般需要显示定义复制构造函数。

  • 声明而不定义成员函数是合法的,但是使用将导致链接失败。将复制构造函数声明为private可防止复制。定义了复制构造函数,也必须定义默认构造函数。

  • 重载赋值操作符=,隐含的第一个参数this。复制构造函数、赋值操作符、显示析构函数【虚空不算】一般同时出现。合成析构函数并不删除指针成员指向的对象。即使编写了自己的析构函数,合成析构函数仍然运行。

  • 默认构造函数不全,会调成员的默认构造函数,复制构造函数不全就不行了。

  • 使用计数是管理智能指针类的通用技术。

  • 重载操作符:保留字operator后面接需要定义的操作符符号,返回类型和参数表和其它函数一样。只能重载已有的操作符,只针对类类型,最好重载原本对类类型无意义的操作符。

  • 使用算法sort等时会假定一些操作符(<)存在,此时要为类型定义重载操作符。重载操作符如果是成员函数,左操作数必须是该类的对象【一定是this】。

  • 用函数或类操作某个类的数据可设置其为友元。== <的逻辑定义如果不一致,不定义<比较好。()是调用操作符,有这个的类称为函数对象,行为类似函数。函数对象作为算法实参,比函数更灵活。标准库中有几个函数对象。

  • 函数对象的函数适配器:绑定器 bind1st将给定值绑定到二元对象函数第一个实参 bind2nd 第二个实参。 求反器 not1 not2 count_if find_if

  • 转换操作符:operator type() type表示内置类型名,类类型名或别名。【operator int(); //重载类型转换操作符,可以做到int i; Integer it; i = it; //it直接转为int类型,然后赋值给i 例如:Integer::operator int() {return data;data是Integer的私有成员}】

  • virtual函数是基类希望派生类重新定义的函数,希望派生类继承的函数不能为虚函数。根类一般要定义虚析构函数。

  • 派生类只能通过派生类对象访问protected成员,不能用基类对象访问。基类定义为virtual就一直为虚函数,派生类写不写virtual都是虚函数。用做基类的类必须是已定义的。

  • 存在虚函数+指针或引用==产生多态。非虚函数编译时就按指针或引用或对象类型确定。可以使用域操作符强制调用基类虚函数【虚中调虚】。基类虚函数和派生类的默认实参要一致。

  • 派生类继承基类的访问控制标号【何种方式继承】无论是什么,不影响派生类使用基类成员,但影响使用派生类的用户访问基类成员。类使用接口继承还是实现继承对派生类用户具有重要含义。

  • 友元关系不继承。

  • 派生类指针可自动转换到基类指针,反之不行。如果知道基类到派生类的转换【这种转换是基类地址赋给派生类指针】是安全的【就是说心里清楚基类指针指向的确实是派生类】,可以使用static_cast强制编译器进行转换。dynamic_cast是在运行时进行检查。

  • 构造函数无法继承,派生类构造数还要初始化基类【否则只能用合成构造函数初始化】。初始化列表和初始化的顺序无关。只能初始化直接基类。

  • 赋值操作符必须防止自身复制【赋值之前会先释放自身的内容,万一是自己, 那不就丢失了】。派生类析构函数不负责清除基类成员,每个析构函数只负责清除自己成员。派生类指针的静态类型和动态类型不一致时【基类指针指向派生类是时】,为保证删除指针调用合适的析构函数【多态】,基类析构必须为virtual。

  • 构造函数是对象动态类型确定之前运行的,不需要定义为virtual。

  • 引用、对象、指针的静态类型决定了能够完成的行为,动态类型有多的功能也无法使用。派生类应避免与基类成员名字冲突。局部作用域中声明的函数不会重载全局域的函数。派生类定义的函数也不重载基类函数【想重载要么不定义,要么全定义】。using作用域。

  • 纯虚函数抽象类无法创建对象 派生类对象复制到基类时派生类对象将被切掉【而指针和引用不会】。

  • 对象不支持动态绑定,指针和引用支持但使用起来麻烦,解决方法是定义包装类或句柄类【提供到其它类接口的类】。像使用指针一样使用句柄而不用管理它指向的对象。类似智能指针的方法建立指针句柄。

  • 关联容器的构造函数是我们能够提供比较函数的名字:std::multiset<Sales_item, Comp【比较器】> items(compare【比较函数】);

  • template <typename T>模板定义以关键字template开始【旧程序中可能用class】,后接模板形参表,模板形参表是由尖括号扩住的一个或多个模板形参的列表,以逗号分隔。表中可以有非类型形参,实例化时绑定值。

  • 通过在成员前面加上typename告诉编译器将成员当做类型。泛型代码两个原则:1.模板形参是const引用 2.函数体中只用<比较

  • 模板形参数量自由,可以设定返回值为一个形参。显示提供实参:long a=sun<long>(i, lng)

  • export关键字能够指明给定的定义可能会需要在其他文件中产生实例化。非类型形参的模板实参:template<int hi, int wid> 实例化时必须是常量表达式 Screen<24,80>

  • 模板中的友元表示任何实例可以访问任何实例。模板类中可以有模板类成员。

  • 模板类中的static成员由同一实例化的对象共享,但不同模板形参的实例化对象间不共享。

  • 模板特化:template<> 模板名<模板形参>函数形参表 函数体 特化类 也可以只特化类中某个成员 部分特化:多个模板形参,特化某个形参【编译器会优先选择特化的】。匹配同样好时,非模板版本优先。

1人推荐
随时随地看视频
慕课网APP