c++ note

1.函数可以返回指针类型,表示为,如:int *Fun(){....}
2.函数参数里面加个void表示什么参数都没有,即Fun()和Fun(void)是等价的
3.函数形参是被分配内存空间的,这一点与函数内部局部变量本质上是一样的;而普通类型形参的传递便是实参内容的复制,也就是用实参的值初始化形参;引用类型的形参则跟引用类型的变量一样,是实参的别名
4.int *p = 0;是把指针变量p初始化为0值,而不是把指针指向的值初始化为0值;
5.指针一定要初始化。对于大多数编译器来说,如果使用未初始化的指针,会将指针中存放的不确定值视为地址,然后操纵该内存地址中存放的内容,通常会导致程序崩溃。
6.NULL是c++从c继承而来的预处理器变量,该变量在cstdlib头文件中定义,其值为0。如果在代码中使用了这个预处理器变量,则编译时会自动被0值替换。因此,把指针初始化为NULL等效于初始化为0值。
7.不像可以把double类型赋值给int类型,double类型地址是不能赋值给int类型指针的。
8.由于指针的类型用于确定指针所指对象的类型,因此初始化或赋值时必须保证类型匹配。但有一个例外void *类型指针,任何类型的指针都可以赋值给void *类型指针。
9.*p 操作就是对指向的对象进行操作,把它直接看做一个整体,不要考虑指针
10.引用和指针的两个重要区别:(1)引用总是指向某个对象:定义引用时没有初始化是错误的。(2)赋值行为的差异: 给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象关联。引用一经初始化,就始终指向某一个对象(这就是为什么引用在定义时必须初始化的原因)
11.查看5.cc
12.一个指向const对象的指针,可以被赋值以指向别的对象,但不允许用这个指针去修改初始const对象的值,另外它可以不被初始化。p表示单独对指针操作,*p表示对指针指向的对象操作。对于一个指向const对象的指针p,可以用它进行任何操作,包括指向一个非const对象,但是不要使用*p进行操作,因为*p是对它指向的const对象进行操作,这是不允许的。
13.不要用一个普通指针指向一个const对象,这个是不允许的。
14.const指针的定义方式是在*后加个const
15.指向const对象的指针可以在定义时不初始化,但其它的普通const类型变量在定义时都要初始化
16.int *pia = new int[10]() 表示这十个元素都被初始化为0;new返回的是指针;new后面是类型,可以是内置类型,也可以是自定义类型;new与类型后面的括号无关,new后面的部分可以认为是独立的,含义就是在声明一个某类型对象,不加括号或加个空号只不过是表示采用该类默认构造函数,加个带内容的括号则表示由这个实参来决定调用哪个构造函数。
17.在某文件中定义的const变量,默认为本文件局部变量,别的文件想要访问可以用extern
18.一个普通形参可以用普通实参或const实参赋值,但是指针类型普通形参不能用const对象地址赋值
19.如果形参是const的,那么在函数内部,这个形参的值也是不允许改变的
20.对于从标准输入读取string,开头的空格及制表符会被忽略掉,另外,直到读到空格为止,即,如果句子中间有空格的话,将只截取空格前面的部分
21.size_type属于配套类型,它是机器无关的,可以保证足够大以能存储任意长度的string对象;size()函数返回的结果就是size_type类型,但是它绝不是int类型,因此也不要把size()结果赋给int类型;在使用size_type的时候,要加上作用域操作符来使用,如string::size_type
22.vector不能通过下标来赋值,而是用push_back函数。下标只是用来读的。
23.迭代器iterator有点类似于size_type,它属于配套类型,即隶属于某个特定的标准类
24.一个数组形参可以定义为以下三种方式:void printvalue(int *a);void printvalue(int a[]);void printvalue(int a[10]);其中第三种方式不可取,因为当实参个数达不到10个时,函数调用就可能会产生越界。
25.函数返回值实际是个临时对象,别处使用这个返回值时会发生内容复制,因此当函数返回的内容很大时可以采用返回引用的方式(const 引用);但有一个种情况需要特别注意,不要返回函数内部局部变量的引用,因为当函数返回的时候,局部变量内存空间实际已经释放了,这将会导致引用指向不确定的地址;同样的道理,也不要返回指向局部变量的指针
26.如果一个形参具有默认实参,那么,它后面所有的形参都必须有默认实参
27.所谓常量成员函数,也叫做const成员函数,是指跟在形参表后面加个const。这个const的作用是说明不允许用这个函数修改此类对象的数据成员。推而广之,也就是不允许修改此类对象,也就是说,*this是不允许修改的,*this是const的。
28.没有形参的构造函数称为默认构造函数。默认构造函数分为编译器创建的默认构造函数和显示定义的默认构造函数两种。
29.void F() const.这样的类成员函数表明不允许通过这个函数修改类数据成员
30.const char * F() 表示返回一个char指针,是个常量指针,const修饰的是指针而不是指针指向的对象,这与定义变量时有点不一样。
31.在类的成员函数中,this是以隐含形参的方式供成员函数用的。this就是调用者的指针。
32.this本身默认是一个const指针
33.基于成员函数是否是const,可以重载一个成员函数;同样的,基于一个指针形参是否是const,可以重载一个函数。
34.cin cout都是标准库iostream定义的标准输入、输出对象
35.初始化与赋值不是一回事。初始化分为直接初始化和复制初始化。通常复制初始化效率更高。
36.必须对任何const或引用类型成员以及没有默认构造函数的类类型成员使用初始化,也就是在构造函数上加初始化列表;构造函数在被调用以构造类对象前,类的数据成员首先已经构造完成。
37.构造函数跟普通函数一样,也可以使用默认实参,而且通常是常用的方式。
38.一个类哪怕只定义了一个构造函数,编译器也不会再生成默认构造函数。
39.问题:什么是默认构造函数?默认构造函数就是没有显式提供初始化时调用的构造函数,它由不带参数的构造函数,或者为所有形参提供默认实参的构造函数定义。如果定义某个类的变量时,没有提供初始化式,就会使用默认构造函数。如果用户定义的类中,没有显式定义任何构造函数,编译器就会默认为该类生成默认构造函数,称为合成的默认构造函数。
40.使用默认构造函数定义类对象的方法是 string s;而不是 string s(),因为后面加了括号,编译器会把s当成函数。但是可以用另外一种方式,string s = string();
41.拷贝(复制)构造函数的定义:只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),注意,必须是对本类类型的引用,否则编译都通不过。
42.复制初始化总是调用复制构造函数
43.explicit(显式的)用于类内部的构造函数声明上,表示禁止以隐式转换的方式调用构造函数;所谓显式的调用,我的理解就是使用类名
44.如果我们自定义了复制构造函数,编译器仍然会提供默认复制构造函数;析构函数也一样,即便我们自定义了析构函数,系统仍然会提供默认的析构函数,所以通常没有必要自定义析构函数
45.定义如何进行初始化的成员函数,称为构造函数
46.不要在while循环条件中使用自增操作符
47.自增操作符作为条件时(如if或while的条件),自增操作符是在执行语句体之前完成的,不过++i和i++仍遵循它们不同的优先顺序。
48.定义容器对象时,可以使用形参为两个迭代器的构造函数,这两个迭代器不一定必须是此种容器类型。比如vector<int> iv,则在定义list对象时,可以用list<int> il(iv.begin(),iv.end())
49.定义一个iterator为iter,则iter->....或(*iter).就是等于调用本容器“元素类型”的相关函数,换句话说,对iter的操作就等于直接操作元素
50.list只支持自增、自减和相等、不等运算,算术运算和关系运算都不支持
51.引用pair成员的方法是直接用解引用加first或second,注意,pair不是容器
52.对于map类型,能作为键值的唯一条件是它可以做关系运算<s
53.谨记map下标操作的副作用:当该键不在map中时,下标操作会插入一个具有该键的新元素
54.set容器里的元素是只读的,而list里的元素是可以修改的
55.set,map容器的元素是自动按升序排列的,比如单词就是按a-----z
56.map容器的下标操作可以自动将不存在的元素添加进去,并且返回指向mapped_type的迭代器,也就是指向map值元素的迭代器
57.set容器不支持下标操作
58.不要返回局部对象的引用
59.当在一个const函数中调用set,map的迭代器时,要用const_iterator而不是iterator
60.vector<int> vi; vi.reserve(10);    其中reserve仅仅分配内存,此时vi仍为空对象,不能对vi的元素进行操作
60.unique算法删除“相邻”的重复元素
61.谓词函数必须有两个参数,而且参数类型必须与容器元素类型一致
62.构造函数初始化式只在构造函数定义中而不再声明中指定
63.构造函数初始化式与在构造函数中给成员赋值是有区别的,其区别就好比定义变量时初始化与给变量赋值
64.对于没有默认构造函数的类类型成员,以及const或引用类型的成员,都必须在构造函数初始化列表中进行初始化
65.由于编译器提供了默认的复制构造函数,所以通常我们不需要自己定义复制构造函数,但是当类成员有指针变量时或者有成员在构造函数中需要分配资源,必须自己定义复制构造函数
65.成员被初始化的次序是类内定义成员的次序,而与初始化列表的次序无关,因此,初始化列表的次序尽量与定义成员的次序一致
66.构造函数初始化列表可以用成员甲初始化成员乙,但是要保证甲首先被初始化,因此,在成员定义时,甲成员应该放在乙前面
67.如果某个构造函数的形参都有默认实参,那么这个构造函数同时也作为默认构造函数,因此,不能为某个类定义两个形参都具有默认实参的构造函数,这样就成重复定义了
68.如果一个类没有默认构造函数,则在编译器需要隐式使用默认构造函数的环境中,该类就不能使用,所以,如果一个类定义了其他构造函数,通常也应该为其定义默认构造函数
69.复制构造函数也可以使用初始化列表
70.复制构造函数中如果没有初始化某个类成员,则这个成员用该成员的默认构造函数初始化。复制构造函数中的默认初始化不会使用成员的复制构造函数。
71.delete删除一个指针,实际是释放指针指向的对象的内存空间,而该指针仍然存放了它之前所指向对象的地址,此时,这个指针就被称作悬垂指针,一个有效的做法是将这个指针赋值0,这样就告诉这个指针不再指向任何对象。
72.virtual只能用在类内部函数的声明上,不能用在类外部函数的定义上;virtual指明该函数在运行时确定,以区别于普通成员函数的编译时确定;构造函数不能定义为virual;一般希望派生类重定义的函数都声明为虚函数;虚函数并不是说基类不用实现,只不过当派生类重定义了该函数时,派生类将使用自己的定义,否则使用基类的定义
73.对于基类的public和private成员,派生类与普通外部用户具有同样的使用权限,那么为了使派生类能访问基类成员但又不想让外部用户访问,则基类的这些成员可以声明为protected。
74.派生类只能通过派生类对象访问基类的protected成员
75.派生类必须对想要重定义的每个继承成员做出声明
76.派生类中虚函数的声明必须与基类中的定义方式完全匹配,但是如果基类的虚函数是返回基类的引用或指针,则派生类在重定义的时候可以返回派生类的引用或指针
77.一旦函数在基类中声明为虚函数,它就一直为虚函数,派生类无法改变该函数为虚函数这一事实。派生类重定义虚函数时,可以使用virtual保留字,但不是必须这样做。
78.有一种常见的误解认为用struct保留字定义的类与用class定义的类有很大的区别。其实唯一不同的只是默认的成员保护级别和默认的派生保护级别。
79.友元关系不能继承。基类的友元并不就是派生类的友元,它对派生类的成员没有特殊访问权限。一个被当做友元的类作为基类的话,该基类的派生类也不被当做友元。
80.如果基类定义了static成员,则整个继承层次中只有一个这样的实例。如果基类中static成员是private,则同样的,派生类也无法访问。
81.在类的继承中,访问标号private,protected,public的作用是规定“派生类继承自基类的成员”作为派生类成员的访问权限,也就是说这个权限是给使用派生类对象的外部用户用的,它对派生类访问基类成员的权限没有任何影响,派生类对基类成员的访问权限仅仅取决于基类对成员的权限控制,换句话说,派生类对基类成员的访问跟普通外部用户对基类成员的访问具有一个样的权限。
82.基类的protected成员仅仅是开放给派生类本身,而不是派生类对象,也就是说可以在派生类的成员函数里访问基类的protected成员,但是不能通过派生类对象访问基类的protected成员。
83.在函数体外定义的变量都初始化为0,在函数体里定义的内置类型变量不进行自动初始化。类的内置类型成员除非由初始化列表初始化,否则也不自动初始化。
84.派生类构造函数的初始化列表只能初始化派生类成员,不能初始化继承成员。
85.一个类构造函数中只能初始化自己的“直接”基类。
86.假如基类拥有private成员,照理说派生类也拥有了该成员,但实际上派生类却无法直接访问该成员,可以这样理解,该成员是属于基类的,而基类与派生类在本质上是两个类。
87.操作符被重载为类成员的方法是它的操作数至少有一个是类类型(引用),而并非看是否定义在类定义体内部。

88.初始化列表用冒号,而不是用作用域操作符

89.const成员函数不能改变其所操作的对象的数据成员

90.函数的()名为调用操作符

91.struct和class是有区别的,struct的默认访问标号是公有的,而class默认是私有的

92.内联函数(inline)调用时直接展开,类似于宏,所以省去了函数调用的开销,正因为如此,内联函数的定义对编译器而言必须是可见的,所以内联函数必须在头文件中定义,另,内联函数并不只用在类中,普通函数也可以定义为内联函数

93.定义类时通常先定义该类的函数成员,由函数成员决定需要定义哪些数据成员。

94.ifstream in;in >>string

95.#define #ifndef都是预处理器的一部分,在编写头文件时,一个良好的习惯是使用上述预编译指令

96.将一个const对象传递给普通的引用类型的形参是非法的,因为本来这个实参是允许修改实参的,但用于修改const实参是非法的,因此,通常应该将函数内不需要修改的引用形参定义为const引用。

97.函数形参列表后面跟const的形式只在类成员函数中出现,它的作用是规定this是const类型,这也是这种常量成员函数无法改变调用对象的原因。

98.一个返回类类型的const成员函数,必须返回const类型引用

99.如果自定义了一个构造函数,则不再提供合成默认构造函数,但仍提供合成复制构造函数,复制构造函数也是构造函数的一种

100.复制构造函数不是必须的,如果一个类只包含类型成员或内置类型成员,是不需要显示定义复制构造函数的,用合成的复制构造函数就可以的

101.析构函数有这个特点:系统总会合成一个默认析构函数,换句话说,即使不显式定义析构函数,系统也会提供一个默认的析构函数,而且如果显式定义了一个析构函数,那么系统调用完这个析构函数后,仍会调用合成的析构函数

102.理解派生类构造函数后面跟着的基类构造函数,它是派生类构造函数初始化列表的一项,如果派生类有其它成员需要放在初始化列表中,则是用逗号与该项隔开,而不是再加一个冒号

103.字符串字面值,C风格字符串(以NULL结束)互相兼容,他们实质是const char *类型的数组,string提供了c_str()用于获取string的c风格字符串形式,也就是数组的第一个元素的地址

104.delete删除的必须是new分配的内存,他们只工作在堆中,new一个类对象,则该对象的成员也是在堆中,因此也需要用delete才能释放

105.static数据成员应该用单独一行在源文件中赋值,不能放在构造函数中

106.系统默认提供的析构函数不会是虚函数.析构函数只可能显示指定为虚函数.

107.将copy构造函数定义为private,可以阻止外人试图对其对象进行拷贝操作;仅声明copy构造函数,不实现,可以防止类内部或友元对其进行拷贝操作(链接的时候会报错,从而阻止他使用)

108.在子类中调用父类的函数,父类函数中的this指向的仍旧是子类对象

109.类的数据成员不能在类的定义中初始化,无论基础类型还是数组类型

110.命名空间只不过是规定作用域的一种形式,当然,c++设计它的初衷是为程序实现模块化

111.using namespace std;并不是说当前程序代码属于std命名空间,而是说程序代码需要用到std中定义的东西,换句话说,这句话只是赋予当前程序以可以访问std定义的内容

112.异常处理。throw,即产生异常;catch,即捕获异常。若要捕获异常(catch),必先产生异常(throw)。异常的形式可以自己定义,比如定义为一个结构体。

catch是一个关键字,在其后的括号里包含一个声明,其使用方式类似于函数参数的声明,即,该声明描述的是这个catch要捕获的对象的类型,而且也可以为该对象命名,如catch(ERROR err){},有了这个err对象后,在{}中就可以使用err对象的具体内容。异常处理,还可以看作是一种程序跳转方式。

113.假如定义了一个类complex,则complex a = complex(),其执行过程是,complex()构造了一个对象,然后通过拷贝构造函数构造了a对象,换句话说,这里的=并不是操作符=,至于说它调用了拷贝构造函数,这自然是编译器实现的。

114.对一个引用r,也可以使用&r获取指针,当然该指针指向的是它引用的对象的地址

115.左值:可以取得它地址的对象(变量)

116.一个非const的引用,在初始化时,必须指向一个类型完全相同的值,如int i;double d; int& r = i是正确的,而int& r = d是不正确的,但是在普通赋值时,只要值间可以转换,则是正确的,如r=d;一个const的引用,在初始化时则不必类型完全相同,const int& r = d是正确的,而且可以使用常量赋值,如const int& r = 3;注意,从double赋给int型的引用,中间还是有隐式转换发生,因此,int&的结果还是只是整数

117.c++提供了四种强制类型转换,也称为显式转换,static_cast,reinterpret_cast,dynamic_cast,const_cast

dynamic_cast主要用来执行“安全向下转型”(safe downcasting)。也就是用来决定某对象是否归属继承体系中的某个类型。它是唯一无法由旧时语法执行的动作,也是唯一能耗费重大运行成本的转型动作。

static_cast运算符完成相关类型之间的转换。例如在同一类层次结构中的一个指针类型到另一个指针类型,整型到枚举类型,浮点类型到整型等。static_cast用来强迫隐式转换(implicit conversions)。

reinterpret_cast处理互不相关类型之间的转换,意图执行低级转换,实际动作可能取决于编译器,也就是说它是不可移植的。例如从一个整型到指针,或者从一个指针到另一个毫不相干的指针类型。一般而言,它提供了一个新类型的值,并保持其参数原来的二进制模式。

const_cast可以清除const和volatile限制符进行转换。它是唯一有次能力的C++ style转型操作符。

118.既然c++从c继承了隐式类型转换,为什么还要提供强制类型转换呢,举个例子,int i;double d;int j = i + d;,其中i+d会隐式的把i转换为double,但是如果i+static_cast<int>(d),则编译器就省却了对i的隐式转换

119.const char* name = "abc"; name = "def";
这是正确的,因为name是一个指向常量的变量,它本身指向谁是不受限制的

120.运算符函数的名字是由关键字operator后跟对应的运算符组成的,例如operator +.运算符函数的定义和使用都可以像其它函数一样。使用运算符不过是显示调用运算符函数的一种简写形式,例如complex a, b,c; c = a + b与a.operator+(b)是一样的

121.二元运算符,即,两者参与运算。可定义定位为类的成员函数(必须是非静态的),带有一个参数;也可以定义为非成员函数,带有两个类类型的参数。但是,operator=,operator[],operator(),operator->只能作为非静态的成员函数,这是为了保证它们的第一个运算对象是左值。另外,赋值(=)和取地址(&)都有默认的预定义。

122.一个运算符函数或者是一个成员函数,或者是非成员函数但是至少有个参数是类类型的;如果想用某个基本类型作为第一个参数,那么自然不能定义为成员函数了

123.假如类complex有个int成员,且complex有一个complex(int)构造函数,那么complex b = 3的意思是complex b = complex(3)。即符合条件的默认构造函数会被调用。当程序里需要一个某类型的值,而该类的某个构造函数又可以通过把提供的值作为初始化式或者被赋的值,则这个时候,合适的构造函数将会被调用

124.下标运算符函数operator[],一般可定义为double complex::operator[](int arg),其中arg便是下标,对类complex的某对象c调用下标操作符:c[arg]。注意,函数operator[]()必须是成员函数。

125.“函数调用运算符”函数operator(),跟下标运算符类似,定义的形式为operator()(arg).经常用在算法中。既然是类的成员函数,自然也是通过对象调用的(),像类似for_each(v.begin(),v.end,complex()),实质是complex构造了一个对象,然后for_each反复使用该对象调用其函数调用运算符。定义了operator()的类被成为函数对象(注意:名叫对象,但实际是个类)

126.赋值运算符是不被继承的

127.虚函数必须是通过指针或引用调用才具备多态性,如果直接通过对象调用,则它的确切类型就已经为编译器所知,因此也就不需要运行时的多态性了

128.基类定义有某非虚拟的成员函数,如果派生类定义有相同名字的成员函数,即使函数返回值或参数不同,那么采用派生类的成员函数将会覆盖掉基类的成员函数

129.数组的引用template<class T, size_t  N>  void array_init(T (&param) [N])

130.

template<class Iterator>
    struct iterator_traits {
        typedef typename Iterator::iterator_category iterator_category;
        typedef typename Iterator::value_type value_type;
        typedef typename Iterator::difference_type difference_type;
        typedef difference_type distance_type;
        typedef typename Iterator::pointer pointer;
        typedef typename Iterator::reference reference;
    };

定义一种迭代器,目的是为了使用它去操作元素和序列,因此必然要为它定义相关的类型成员,上述中Iterator::**就是它的类型成员。

interator_traits只不过是封装了这些类型成员而已

131.explicit ,只用在类内部构造函数的声明上,用来指示编译器不能隐式调用该构造函数生成对象

132.如果给类定义了带参的构造函数,则最好同时给它定义不带参的默认构造函数,因为很多时候都需要直接用默认构造函数定义对象

132、<< 名为“输出操作符”;>> 名为“输入操作符”
133、left << right,其中left称为“左操作数”,right称为“右操作数”,而<<操作符返回左操作数对象,也就是流对象,因此可以连续使用 <<,比如cout << a << b,为了好理解,其实它也可以写作(cout << a) << b
134、endl不是换行符,应该叫做操作符或操控符,因为它除了换行外,还刷新流缓冲区
135、iostream仅用于控制台窗口,fstream用于文件,sstream用于字符串
136、到目前为止,所描述的流类读写的是由char类型组成的流。此外,标准库还定义了一组相关的类型,支持wchar_t类型。每个类都加上“w”前缀,以此与char类型的版本区分开来。于是,wostream,wistream和wiostream类型从控制窗口读写wchar_t数据。相应的文件输入输出类是wifstream,wofstream和wfstream。而wchar_t版本的string输入/输出流则是wistringstream,wostringstream,wstirngstream。标准库还定义了从标准输入输出读写宽字符的对象。这些对象加上"w"前缀,以此与char类型的版本区分:wchar_t类型的标准输入对象是wcin;标准输出是wcout;而标准错误则是wcerr。
每个IO头文件都定义了char和wchar_t类型的类和标准输入/输出对象。基于流的wchar_t类型的类
和对象在iostream中定义,宽字符文件流类型在fsteam中定义,而宽字符stringstream则在sstream
头文件中定义。
137、IO对象不支持复制,也不能被用来赋值,所以说IO对象不能放在容器中,因为容器元素类型必须支持复制;IO对象也不能作为函数的参数或返回值,如果用的话,必须使用它们的指针或引用
138、流只有在处于无错误状态时或没有遇到文件结束符时,才可用于输入输出,对错误状态的判断如if (cin >> val)、while(cin >> val)
139、逗号操作符,首先计算它的每一个操作数,然后返回最右边操作数作为整个操作数的结果
140、流的四种状态good/bad/fail/eof,分别由流的badbit/failbit/eofbit成员标志,分别由good()/bad()/fail()/eof()获取,good表示没有错误状态,bad表示发生了系统级错误,如果发生了这种错误,则通常流就不能再使用了,fail表示可恢复的错误,比如本来希望一个整数值,结果输入了一个字符串,恢复,也就是将这个状态值设为false,使用clear函数,eof表示到达文件尾
141、ifstream在读取失败时,再次尝试读取,仍会读原来的地方,如下:

    ifstream ifile("1.dat");

    while (ifile >> val, !ifile.eof())
    {
        if (ifile.bad())
            throw runtime_error("IO System Error.");
        else if (ifile.fail())
        {
            cerr << "bad val, try again";
            ifile.clear(ifstream::failbit);
            // ifile.clear();
            // ifile.ignore(20, ' ');
            continue;
        }
    }
比如文件中有个非数值型字符,导致file.fail(),之后continue到ifile >> val,此时仍旧读取这个字符,仍会导致fail.
解决此问题的方法就是忽略内容,ignore函数将忽略掉接下来的20个字符,或者直到遇到' '。注意:VC编译器没有正确实现clear(istream::failbit),必须采用clear()

142、每个IO对象都有一个缓冲区,读写的数据就存在缓冲区中。将数据从缓冲区中写入到设备或文件,被称作刷新缓冲区。导致刷新缓冲区的动作有:
(1)程序结束
(2)缓冲区已满,系统自动刷新
(3)用操控符手动刷新,如endl和flush,其中flush只比endl少了个'\n'
(4)使用unitbuf操控符
其实,控制台窗口会定时自动刷新缓冲区,这就是为什么不用上述操控符,控制台窗口也能显示内容的原因
143、用ifstream对象打开一个文件可以有两种方式
(1)使用带文件名为形参的构造函数:ifstream ifile("1.dat")
(2)定义一个ifstream对象,然后使用open成员函数

144、文件流在使用完毕后应该调用close,close并不会改变流的内部状态,也就是说,如果再次open,流原来的状态还存在,要清除所有状态,就需要使用clear
145、fstream::app打开模式可以保证每次写入文件都在文件尾写入
146、迭代:即遍历
147、控制台窗口输入结束Ctrl+z
148、二进制计算
(1)十进制中有十位、个位,与此类似,二进制中有二位、个位,其中个位只有一个1
(2)每一位均进一位(本位:0->1/1->0 上一位: 0->1/1->0),则数值成了原来的2倍
(3)1111 1111 与 111 1111 与 1000 0000
    (a)、111 1111 + 1,所有位依次进1,于是构成1000 0000,即1000 0000 = 111 1111 + 1
    (b)、因为1111 1111 = 1000 0000 + 111 1111 = 111 1111 + 1 + 111 1111,即1111 1111 等于2倍111 1111再加 1
149、8位,无符号是0-255,有符号是-128 - 127
150、整数0的补码的推导:
正数的原码,补码,反码相同。
正整数0的原码,补码,反码相同,全0

负整数0的原码,符号位1,其它位0 -- 10000000  (假定共8位,最高位是符号位)
负整数0的反码,符号位不变,为1,其它位0变1,-- 11111111
负整数0的补码,等于它的反码加1 --
11111111 + 1 = [1]00000000
方括号里的1因溢出,自动丢失,成为 00000000

所以正整数0和负整数0的补码相同,00000000

151、一般而言,float占32位,double占64位,long double占128位,float只能表示6位有效数字,double可以表示10位有效数字
152、浮点数字面值后面不能带有U, 因为浮点数不能使用unsigned修饰,这并不是说浮点数不能是负数
153、编写构造函数的目的是为了初始化成员
154、int j = k = 2;不对,应分开定义
155、内置类型变量的默认初始化,只有在函数外定义的变量才会默认初始化为0
156、不指明构造函数定义一个类对象,实际还是调用了构造函数,只不过是它的默认构造函数
157、const int n;不合法,因为const变量必须初始化
158、引用在定义时被绑定到某对象,之后就无法将其绑定到其它对象,如:
    int i = 1;
    int &j = i;
    
    j = 2;

    int k = 3;
    j = k;// 不要以为j改为指向了k
    j = 4;
    // i最终等于4,而k依然是3

   关于这一点,可以引申至另一个知识点,那就是,函数的引用类型参数,在函数内部改变形参的值,就等于改变了实参的值,但是,在函数内部将某变量赋值给该形参,并不是等于将该形参改引用为此变量,之后改变该形参的值依旧改变的是实参的值而不是这个变量的值,也即,引用一经在定义时绑定到某对象,就无法再指向别的对象,对于函数传参来说,这个绑定的过程就是发生在传参时。以例子说明:

string s = "ss";
void test_191224_1(string &s_)
{
    s_ = s;    // 实际发生了拷贝,s的值复制给了a,并不是让s_改引用到s
}

void test_191224_2()
{
    string a = "a";
    test_191224_1(a);// 形参s_是指向实参a的引用
    std::cout << "a=" << a << endl;    // a的值变为了'ss'
    a = "b";
    std::cout << "s=" << s << endl;    // s依然是'ss'

    string &ra = a;
    test_191224_1(ra);
    std::cout << "a=" << a << endl;    // a的值变为了'ss'
    ra = "b";
    std::cout << "s=" << s << endl;    // s依然是'ss'
}

159、引用符号&、指针符号*跟变量放在一起,这种形式不易引起误解,尤其当一个语句语句中定义了多个变量时
160、const int &a = 1;// 合法,尽管定义为引用有点多此一举
    int &b = 2;// 非法,因为b没有绑定到任何对象

161、const引用绑定的对象不必必须是const对象,但是const引用不允许被赋值;非const引用不能绑定到const对象
162、非const引用只能绑定到严格同类型的变量,const引用则可以绑定到可以隐式转换的类型的变量,如:
    double d = 33.3;    
    const int &i = d;// 合法

    double d = 33.3;
    int &i = d;// 非法
原因是编译器在解释上述程序为
    double d = 33.3;
    int temp = d;
    int &i = temp;
即对i的改变,并不会引起d的改变,所以编译器直接不支持此操作

163、不带任何参数的构造函数称为默认构造函数,所有参数都提供默认值的构造函数也是默认构造函数
164、无论cout << int;还是cout << string;调用的<<都是ostream的<<操作符,而不是全局或string的<<操作符
165、string支持下标操作符
166、vector<int> v = {0, 1};这是错误的,vector的构造函数显然不可能有{0,1}这种样式
167、for(vector<int>::size_type i = 0; i != v.size(); i++)
(1)尽量使用size_type类型
(2)在c++中,像size()这种库函数几乎都定义为inline函数,所以调用它的运行代价是比较小的
168、int a[2] = {0,1}; int b[] = a;//错误,不能使用数组初始化数组
169、const int i = 0, *p = i;等效于const int i = 0;const int *p = i;
170、const int i;//非法,i必须初始化
    int const *pi;//非法,pi是一个const指针,必须初始化
171、const类型的变量一经初始化后,不能再被赋值
172、dynamic_cast
其实c++推荐使用自身的虚函数机制实现多态,dynamic_cast并不是推荐的方式,在这里只当学习了,具体的用法可以参考C++手册。
一般使用c++的虚函数机制实现多态编程就可以了,那么什么时候需要使用dynamic_cast呢:
要为派生类增加一个新函数,并且程序中要用基类指针或引用调用该函数,但是没有基类的代码,无法在基类中添加相应的虚函数,那么这时候就可以使用dynamic_cast了,传入派生类引用或指针,转换为基类引用或指针。
173、typeid有点类似于c#中的typeof,获取表达式的类型,其返回值是一个TypeInfo类对象
174、const_cast,去掉表达式的const属性
175、static_cast,编译器支持的任何隐式类型转换都可以由static_cast完成,基本就是告诉编译器别报警告(比如丢失精度时)
176、reinterpret_cast,在二进制级别进行的转换,基本不用,太危险

177、运行析构函数仅仅是撤销对象,并不释放对象所在的内存
178、allocator类
new一个对象的时候,一是分配了足够的内存,而是调用了相应的构造函数构造了对象,但有时候构造这个对象可能是多余的,因为程序也许永远不用这个对象。
那么如果想仅仅分配内存,不构造对象,就可以使用allocator类。像stl容器大多都是自增长的,大多都采用这个方法

179、模板的非类型模板形参
template<class T, int i>
const T& Func1(const T &t, i)
{
    cout << i << endl;
    cout << t << endl;

    return t;
}
以上用法是不对的,模板非类型形参跟普通函数形参一样,相当于定义了个局部变量,作用域仅在函数体内部,而不包括函数参数,也就是说函数参数中的i并非
是模板参数中的i
而是Func1定义的一个普通函数形参,却没有指定类型,因此编译错误
以上用法可以改为:
template<class T, int i>
const T& Func1(const T &t, int j)
{
    cout << i << endl;
    cout << t << endl;
    cout << j << endl;

    return t;
}
--------------------
template<class T>
const T& Func2(const T &t, int j)
{
    cout << t << endl;
    cout << j << endl;

    return t;
}

调用模板函数可以显示指定模板参数的类型,也可以不指定,区别在于编译器能否根据实参推断出形参的类型

如对于Func2,Func2("dd", 3);是正确的,

而对于Func1,必须显式指定形参类型,非类型形参还要指定其值Func1<string, 2>("ss", 1);

180、特化类模板的前提是该模板已存在
181、对于类模板而言,只有那些被调用的成员函数才会被实例化
182、C风格字符串"Hello"的类型是char[6],而不是char*,编译器在需要的时候会隐式把char[6]转换为char*,这被称作“退化”,但没有反向的转换。
183、extern int a = 1;定义了整型变量a,并初始化为1,但是,这种定义只能用在函数外部(全局变量)
184、注意,int a;是一个定义,不是一个声明。extern int a才是一个声明
185、指针类型变量没有隐式转换,赋值或初始化必须遵循严格同一类型
186、int main()
{
    string word;
    while(cin >> word)
        cout << word << endl;
    return 0;
}

输入动作被cin和>>监控,输入的内容进入cin的缓冲区,>>从cin的缓冲区中读取内容,并把内容赋予string 对象word。当遇到无效输入(如回车)或到达文件尾时,操作符>>认为读取结束,并返回它读取的流对象cin的状态,状态有效则while(true),状态无效则while(false).

188、
int main()
{
    string str;
    while (getline(cin, str))
    {
        cout << str << endl;
    }
}

在使用getline时,输入一行内容后,需要输入两次回车,内容才会显示,这是因为,getline的原型是istream& getline( char* pch, int nCount, char delim = '\n' );即它以'\n'作为默认的输入结束符号,
第一次回车恰是提供了'\n',第二次回车才表示输入结束。


189、   

char szTmp[8] = {_T("12345678")};// 错误,因为字符串"12345678"默认多一个'\0',所以它超出了数组的定义大小
char szTmp2[8] = {_T('1'), _T('2'), _T('3'), _T('4'), _T('5'), _T('6'), _T('7'), _T('8')};// 正确

190.

try

{

......

expr();// 这个函数里throw异常

......// 当且仅当expr()没有任何错误时,才会执行到这儿

}

catch()

{

}

191.c++定义对象语法

class A{};

A a;//正确

A a();//错误,编译器会认作函数调用

A *p = new a();//正确,指定调用A的默认构造函数

192.合成的默认构造函数是public的。

193.try{}catch{}语句的花括号是必需的。

194.如果使用等号(=)初始化一个变量,实际上执行的是拷贝初始化(即调用拷贝构造函数),编译器把等号右侧的初始值拷贝到新创建的对象中去。与之相反,如果不使用等号,则执行的是直接初始化。注意:并不是说用了等号就是执行了赋值运算函数,C++Primer39页:很多程序员对于用等号=来初始化变量的方式倍感困惑,这种方式容易让人认为初始化是赋值的一种。

195.拷贝构造函数和拷贝赋值运算符的调用时机

// vs2013console.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <time.h>
#include <cassert>
#include <string>
#include <sstream>
#include <iomanip>
using namespace std;


class Base {
public:
	virtual ~Base() {
		cout << "~Base()" << endl;
	}

	virtual void func1()
	{
		cout << "Base.func1" << endl;
	}
};

class Derived1 : public Base {
public:
	Derived1() {}
	Derived1(const string& n) :name_(n) {
	}
	Derived1(const Derived1& original) {
		cout << "Derived1 copy constructor[" << this << "]" << endl;
	}
	Derived1& operator=(const Derived1& original) {
		cout << "Derived1 =[" << this << "]" << endl;
		return *this;
	}

	~Derived1() {
		cout << "~Derived1()[" << this << "]" << endl;
	}

public:
	string name_;

};


Derived1 test()
{
	Derived1 d;
	// return返回的并不是d本身,而是d的拷贝,即拷贝是发生在函数内部。此处会调用一次拷贝构造函数。
	return d;
}


int _tmain(int argc, _TCHAR* argv[])
{
	Derived1 dd = test();	// 这不是赋值,这是初始化,并不会调用拷贝赋值运算符;
				// 此处并不发生拷贝,dd就是test本次调用的调用点,
				// 也即,本次调用test时其内部产生的那个副本和dd是同一个对象。
	cout << "dd[" << &dd << "]" << endl;

	cout << "1----------" << endl;

	Derived1 ss;
	ss = dd;// 调用了拷贝赋值运算符,注意,拷贝赋值运算符本身就是作为ss的成员函数调用的,
		// 因此这里并不存在所谓dd到ss的拷贝,千万别被=迷惑。
	cout << "ss[" << &ss << "]" << endl;

	cout << "2----------" << endl;

	ss = test();	// 首先在test内部调用了拷贝构造函数,然后在ss身上调用了拷贝赋值运算符成员函数,
			// 因此这里也不存在所谓到ss的拷贝,但是,与上面不同的是,此处调用test时
			// 其内部产生的那个副本和ss就不是一个对象了,这一句执行完之后,test内部那个产生的副本就析构了。

	cout << "3----------" << endl;

	return 0;
}

196.

子类的合成拷贝构造函数中对于基类部分缺省调用基类的拷贝构造函数,但是如果是自定义子类的拷贝构造函数,则子类的拷贝构造函数中对于基类部分的构造缺省采用的是基类的默认构造函数,若要实现基类部分的拷贝,必须在子类拷贝构造函数的初始化列表中显式指定调用基类的拷贝构造函数。
class Base { /* ... */ } ;
class D: public Base {
public:
// by default, the base class default constructor initializes the base part of an object。
// to use the copy or move constructor, we must explicitly call that
// constructor in the constructor initializer list
D(const D& d): Base(d) // copy the base members
/* initializers for members of D */ { /* ... */ }
D(D&& d): Base(std::move(d)) // move the base members
/* initializers for members of D */ { /* ... */ }
};

同样道理,如果自定义子类的拷贝赋值操作符,则也要显式调用基类的拷贝赋值操作符。
// Base::operator=(const Base&) is not invoked automatically
D &D::operator=(const D &rhs)
{
Base::operator=(rhs); // assigns the base part
// assign the members in the derived class, as usual,
// handling self-assignment and freeing existing resources as appropriate
return *this;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值