1、C++在cstdlib中定义了EXIT_SUCCESS常量。
2、在C++程序中,大部分出现空格的地方都可以用换行符代替。这条规则的一条例外是字符串字面值中的空格不能用换行符代替,另一个例外是预处理指示中。
3、任何允许有制表符,空格或换行符的地方都允许放注释对。
4、编译器通常不会挑剔头文件名,但IDE有时会。
5、标准库的头文件用<>括起来,非标准库的头文件用””括起来。
6、一般来说,主函数返回一个非0的值表示未获得期望的结果。
1、在“位”这一级上,储存器是没有结构和意义的。
2、通常将8位的块作为一个字节(Byte),32位或4个字节作为一个字(Word)
3、在定义长整型时,应该使用大写字母L,小写字母l很容易和数字1混淆。
4、为了兼容C语言,C++中的所有字符串字面值都由编译器自动在末尾添加一个空字符。
5、程序不应该依赖未定义行为,也不应该依赖机器相关的行为。使用了未定义行为的程序都是错误的,即使能够运行,也只是巧合。
6、左值(lvalue)和右值(rvalue):左值可以出现在赋值语句的左边或者右边,右值只能出现在赋值语句的右边。
7、语言本身没有限制变量名的长度,但是为了阅读和修改,不建议过长。
8、C++支持两种初始化变量的方式:复制初始化和直接初始化。要注意的是,C++中赋值和初始化是两个不同的概念。
9、定义如何进行初始化的成员函数称为构造函数。
10、如果定义某个类的变量时没有提供初始化式,这个类也可以定义初始化时的操作。它是通过定义一个特殊的构造函数即默认构造函数来实现的。
11、变量的定义为变量分配储存空间,还可以为变量指定初始值。在一个程序中,一个变量有且仅有一个定义。
12、声明用于向编译器表明变量的类型和名字。定义也是声明,当定义变量时我们声明了它的类型和名字。可以通过使用extern关键字声明变量名而不定义它。
13、因为常量在定以后便不允许修改,所以在定义时必须初始化(引用也是如此)。
14、在全局作用域定义的const常量只是该文件的局部变量,除非在前面加上extern加以全局说明。
15、当引用初始化后,只要该引用存在,它就保持绑定到初始化时指向的对象,不能修改这种映射关系。
16、C++中,通过定义类来自定义数据类型。类定义了该类型的对象包括的数据和该类型的对象可以进行的操作。
17、编程新手总是忘记类定义后面的分号,这是个很普遍的错误!
18、用class和struct关键字定义类的唯一差别在于默认访问级别:默认情况下,class的成员为private,struct是public
19、因为头文件包含在多个源文件中,所以不应该含有变量或函数的定义。例外是,头文件可以定义类,值在编译时已经确定的const对象和inline函数。
20、const变量应该在一个文件里定义并且初始化,所以在头文件里为它添加extern声明,以便于多个文件共享。
1、通常,头文件里只定义确实必要的东西,请养成这个习惯。
2、因为历史原因以及为了和C语言兼容,字符串字面值和string类型不是同一种类型,这一点很容易引起混乱,编程时一定要注意区分字符串字面值和string数据类型的使用。
3、任何储存string的size操作结果的变量必须为string::size_type类型,特别重要的是,不要把size的返回值赋给一个int类型。
4、当进行string对象和字符串字面值混合连接操作时,+操作符的左右操作数至少得有一个是string类型。
5、C++中有些函数可以声明为内联(inline)函数,编译器在遇到内联函数时会尝试直接扩展相关代码而不是进行实际的函数调用。
1、在函数体外定义的数组,其元素均初始化为0;在函数体内定义的数组,其元素无初始化。不管数组在哪里定义,如果其元素为类类型,则自动调用该类的默认构造函数对其进行初始化。
2、数组下标的正确类型应该是size_t
3、尽管C++支持C风格的字符串,但是尽量要避免。string类型除了增强安全性外,效率也提升了。
4、C++中没有多维数组,所谓的“多维数组”不过是数组的数组罢了。
1、短路求值,不解释。
2、bool类型可转换为任何算术类型,bool值false用0表示,而true则为1
3、用++i而不是i++,前置操作符要进行的工作更少,只需加1再返回结果即可,而后置操作符必须先保存原来的值,以便返回未加1之前的值作为操作的结果。(虽然编译器一般会优化这些,但是请不要过度依赖编译器。)
4、如果指针指向的不是用new分配的内存地址,那么用delete删除它是不合法的。
5、C++保证,删除0值的指针是安全的。
6、Do while 语句别忘了最后的分号。
7、可以在条件判断中定义变量,其作用域在整个条件判断中。
1、新的C++标准要求函数必须显式声明返回值。
2、函数切记不可返回局部变量的引用。
3、给函数的返回值赋值会让人感到惊讶,但当一个函数返回的是一个引用的时候,这是很自然的事情。如果不希望返回值被修改,那么请把返回值声明为const
4、C++规定,主函数main不能调用自身。(标准不允许,具体实现则不确定)
5、默认实参是通过给形参表中的形参指定明确的初始值来指定的。但是,如果一个形参具有默认实参,那么后面的所有形参都得有。
6、设计带有默认实参的函数,一般把最少使用默认参数的实参排在前面,最可能使用默认实参的形参排在后面。
7、通常,应在函数声明中指定默认实参,并将该声明放在合适的头文件里。
8、内联说明(inline)对于编译器来说这只是一个建议,编译器可以选择忽略这个建议。
9、内联函数应该在头文件里定义,这一点不同于其它函数。
10、编译器隐式的将在类内定义的函数认为是内联函数。
1、vector的自增长至n长度时仅拷贝O(n)次,可用平摊分析得到。
2、vector的clear并不会真正释放内存,可与空vector进行swap来达到目的。
1、back_inserter/front_inserter/inserter 与 algorithm库里面的算法搭配非常合适。
1、一个类可以包含若干个共有(public)和私有(private)以及受保护(protect)的部分,在public部分定义的成员可以被使用该类型的所有代码访问;在private部分定义的成员可以被其它类的其它成员访问。
2、所有成员必须在类的内部声明,一旦类定义完成后,就没有任何方式可以增加成员了。
3、构造函数是一个特殊的、与类同名的成员函数,用于给每个类的成员设置适当的初始值。
4、在类的外部定义的函数 必须指明他们在类的作用域中。
5、将关键字const加在函数形参表之后,就可以将成员函数声明为常量,const成员不能改变所操作成员的数据成员。const必须同时出现在声明和定义中,若只出现一次,则会造成编译上的错误。
6、像非成员函数可以重载一样,成员函数也可以进行重载,但是成员函数只能重载本类的其它成员函数。因为只有在同一个作用域内才能发生重载,不同作用域就屏蔽了。
7、在声明和定义处指定inline都是合法的。
8、不在类定义体内定义的inline函数,其定义应该放在含有类定义的同一文件中。(因为编译器必须知道实现细节以扩展代码)
9、在创建一个类的对象以前,必须完整的定义该类。除过定义指向该类的一个指针或引用。
10、(一般而言)定义类型时不进行储存分配, 例外情况类似于这种
struct foo { static int bar() { static int i = 0xff; return i; } };
11、类的定义必须用一个分号结束,分号是必须的,因为在类的定义后可以接一个对象定义列表。
12、成员函数的函数体可以显式使用this指针,但不是必须这么做。
13、构造函数可以重载,只要每个构造函数的形参表是唯一的。
14、初始化const或引用类型数据成员的唯一机会是在构造函数初始化列表中。
15、比效率问题更重要的是,某些数据成员必须要初始化,这是一个事实。
16、必须对任何const或引用类型数据成员以及没有默认构造函数的类类型的任何成员使用初始化式。
17、初始化的次序通常无关紧要,但是,如果一个成员根据其它成员而初始化,那么初始化次序是至关重要的。
18、在某些情况下,允许特定的非成员函数访问一个类的私有成员,同时仍然阻止一般性的访问,这是很容易做到的。友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类。友元的声明以关键字friend开始,他只能出现在类的内部的某个地方。通常,将友元声明成组的放在类定义的开始或者末尾是个好主意。
19、友元声明将已命名的类或非成员函数引入到外围作用域中。此外,友元函数可以在类的内部定义,该函数的作用域扩展到包含该类定义的作用域。
20、每个static数据成员只是与该类关联的对象,并不与该类的对象相关联,所以static成员没有this指针,但遵循正常的共有/私有访问规则。
21、像使用任意的类成员一样,在类定义体外部引用类的static成员时,必须指定成员在哪个类中定义。然而,static只能用于类定义体内部的声明中,定义不能标示为static
22、C++支持匿名联合,即定义一个没有确定类型的无名类。这种特殊的联合只能包含非static数据成员,也不能定义构造、析构、成员函数。当在一个命名空间中声明匿名联合时,他就被声明为static类型。
1、复制构造函数(copy constructor)是一种特殊构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类的对象对其进行初始化时,将显式使用复制构造函数;当将该类型的对象传递给函数或从函数返回值返回该类型的对象时,将隐式使用复制构造函数。
2、析构函数(destructor)是构造函数的互补,当对象超出作用域或动态分配的对象被删除时,将自动应用析构函数。可以显式调用析构函数。
3、复制操作符(assignment operator)与构造函数一样,赋值操作符可以通过指定不同类型的右操作数而重载。右操作数为类类型的版本比较特殊:如果我们没有编写这种版本,编译器将为我们合成一个。
4、复制构造函数,析构函数和赋值操作符总称为复制控制(copy control)
5、复制初始化首先使用指定构造函数创建一个临时对象,然后将那个临时对象复制到正在创建的对象。
6、即使我们定义了其他构造函数,也会合成复制构造函数。合成赋值构造函数的行为是,执行逐个成员初始化(memberwise initialize),将新对象初始化为原对象的副本。
7、为了防止复制,类必须显示声明其复制构造函数为private,然而,类的友元和成员依旧可以进行复制。如果连友元和成员函数中的复制也禁止,那就声明一个private复制构造函数但不对其进行定义。那么用户代码中的复制尝试将在编译时标记为错误,而成员函数和友元中的复制尝试将在连接时产生错误。
8、与复制构造函数一样,如果一个类没有定义自己的赋值操作符,则编译器会自己合成一个。
9、重载操作符是一些函数,其名字为operator后跟着所定义操作符的符号。当操作符为成员函数时,它的第一个操作数隐式绑定到this指针。
10、合成复制操作符与合成复制构造函数一样进行成员的逐个赋值。
11、复制和赋值常常一起使用。实际上,应将这两个操作符看作一个。如果需要其中一个,我们几乎也肯定需要另一个。
12、析(“分崩离析”之意)构函数:当对象的引用或指针超出作用域时,不会运行析构函数。只有删除指向动态分配对象的指针或实际对象(而不是对象的引用)超出作用域时,才会运行析构函数。
13、如果需要析构函数,那么肯定也需要赋值操作符和复制构造函数,这个规则叫三法则(rule of three)
14、析构函数是个成员函数,它的名字是类的名字前面加上一个~符号,他没有返回值也没有形参,所以不可重载。
1、当一个重载操作符的含义不明显时,给操作取一个名字更好,对于很少用的操作,使用成员函数也比重载操作符要好。如果不是普通操作,没有必要追求简洁而用操作符。
2、许多类都要重载输入和输出操作符,一般而言,输出操作符应输出对象的内容,进行最小程度的格式化,它们不应该输出换行符(让类的用户自己控制输出细节)
3、更重要但通常重视不够的是,输入操作符必须处理错误和文件结束的可能性。设计输入操作符时,如果可能,要确定错误恢复措施,这很重要。
4、注意,为了与内置操作符保持一致,加法操作符通常返回一个右值而不是一个引用。 前置++返回引用。
1、在C++中,基类必须指出希望派生类重定义哪些函数,定义为virtual的函数是基类期待派生类重新定义的,基类希望派生类继承的函数不能定义为虚函数。
2、通过动态绑定(dynamic binding)我们能够编写程序使用继承层次中任意类型的对象,无需关心对象的具体类型。
3、在C++中,通过基类的引用(或指针)调用虚函数时,发生动态绑定。引用(或指针)既可以指向基类对象也可以指向派生类对象,这一事实是动态绑定的关键。用引用(或指针)调用的虚函数在运行时确定,被调用的函数是引用(或指针)所指对象的实际类型定义的。
4、保留字virtual的目的是启用动态绑定。除了构造函数外,任意非static成员函数都可以是虚函数。保留字virtual只能在类内部的成员函数声明中出现,不能用在类定义体外部出现在函数的定义中。
5、基类通常要讲派生类需要重定义的任意函数定义为虚函数。
6、尽管不是必须这样做,派生类一般也会重定义所继承的虚函数,如果没有重定义则使用基类中定义的版本。
7、一旦函数在基类中声明为虚函数,它就一直为虚函数,派生类无法改变它是虚函数的这样一个事实。派生类重定义虚函数时,可以使用virtual关键字,但不是必须这样做。
8、友元关系不能继承。基类的友元对派生类的成员没有特殊访问权限。
1、模板分离编译基本被废弃。
2、类模板中
template<typename T> class Vector {
Vector(const Vector& rhs) {} // (1)
};
// Vector 实际为Vector<T>,同样也适用于友元声明。