1、函数调用做了两件事:用对应的实参初始化函数的形参,并将控制权转移给被调用函数。
2、形参出现在函数定义的地方,多个形参之间以逗号分隔,形参规定了一个函数所接受数据的类型和数量。
实参出现在函数调用的地方,实参的数量与类型与形参一样,实参用于初始化形参。
当形参是引用类型时,对应的实参被引用传递,引用形参是对应的实参的别名。
当实参的值被拷贝给形参时,形参和实参是两个相互独立的对象,对应的实参被值传递。
3、函数的形参表可以为空,但是不能省略。
4、非引用形参表示对应实参的局部副本。对该类形参的修改仅仅改变了局部副本的值。一旦函数执行结束,这些局部变量的值也就没有了。
5、使用引用形参,函数可以直接访问实参对象,而无需复制它。
应该将不需要修改的引用形参定义为const引用。普通的非const引用形参在使用时不太灵活。这样的形参既不能用const对象初始化,也不能用子面值或产生右值的表达式实参初始化。
6、当编译器检查数组形参关联的实参时,它只会检查实参是不是指针、指针的类型和数组元素的类型是否匹配,而不会检查数组的长度。
非引用数组形参的类型检查只是确保是参合数组元素具有相同类型的指针,而不会检查实参实际上是否指向指定大小的数组。
7、在含有return语句的循环后没有提供return语句是非常危险的,因为大部分的编译器都不能检测到这个漏洞,运行时会出现什么问题是不确定的。
8、当函数返回引用类型时,没有复制返回值。相反,返回的是对象本身。
const string& shortString(const string& s1, const string& s2){
return s1.size() < s2.size() ? s1 : s2;
}
理解返回引用至关重要的是,千万不能返回局部变量的引用。
const string& manip(const string& s){
string ret = s;
return ret; //错误返回,不能返回局部变量
}
9、引用返回左值:
char &get_val(string &str, string::size_type ix){
return str[ix];
}
int main(){
string s("a value");
cout << s << endl;
get_val(s, 0) = 'A';
cout << s << endl;
return 0;
}
10、千万不要返回局部对象的指针,一旦函数结束,局部对象被释放,返回的指针就成了指向不再存在的对象的悬垂指针。
11、指定默认实参的约束,既可以在函数声明也可以在函数定义中指定默认是惨。但是在一个文件中,只能为一个形参指定默认实参一次。通常,应在函数声明中指定默认参数,并将该声明放在合适的头文件中。
12、只有当定义它的函数被定义时才存在的对象称为自动对象。自动对象,包括形参,都在定义它们的块语句结束时撤销。形参在函数块中定义,因此只有在函数执行结束时撤销。当函数结束时,会释放它的局部存储空间。在函数结束后,自动对象和形参的值都不能再访问了。
13、static局部对象确保不迟于在程序执行流程第一次经过该对象的定义语句时进行初始化。这种对象一旦被创建,在程序结束前都不会被撤销。当定义静态局部对象的函数结束时,静态局部对象不会撤销。在该函数被多次调用的过程中,静态局部对象会持续存在并保持它的值。
14、内联函数应该在头文件中定义,这一点不同于其他函数。内联机制适用于优化小的,只有几行的而且经常被调用的函数。
15、类的所有成员必须在雷丁的花括号里面声明,此后,就不能再为类添加任何成员。
16、每个成员函数都含有一个额外的、隐含的形参this。在调用成员函数时,形参this初始化为调用函数的对象的地址。
17、一个类可以含有多个构造函数,每个构造函数必须有与其他构造函数不同数目或类型的形参。如果将构造函数定义为private的,则不能定义类的Sales_item的对象,这样的话,这个类也就没什么用了。
18、一般来说,局部地声明函数是一种很不明智的选择。函数的声明应该放在头文件中。
19、仅当形参是引用或指针时,形参是否为const才有影响。如果形参是普通的引用,则不能将const对象传递给这个形参。
20、函数指针是指指向函数而非指向对象的指针。函数类型由其返回类型以及形参表确定。而与函数名无关。
函数指针只能通过类型的函数或函数指针或0值常量表达式进行初始化或赋值。
21、IO标准库类型和头文件
iostream istream从流中读取
ostream写到流中
iostream对流进行读写;从istream和ostream派生而来
fstream ifstream从文件中读取;由istream派生而来
ofstrean写到文件中去;由ostream派生而来
fstream读写文件;有iostream派生而来
sstream istringstream从string对象中读取;由istream派生而来
ostringstream写到string对象中去;由ostream派生而来
stringstream对string对象进行读写;由iostream派生而来
22、IO对象不可复制和赋值。①只有支持复制的元素类型可以存储在vector和其他类型容器里。②形参和返回类型也不能为流类型。如果需要传递或返回IO对象,则必须传递或返回指向对象的指针或引用。
23、修改string对象的操作
s.append(args) 将args串接在s后面,返回s的引用
s.replace(pos, len, args) 删除s中从pos开始的len个字符,用args指定的字符替换之
s.replace(b, e, args) 删除迭代器b和e标记的范围内所有的字符,用args替换之
24、string类型的查找操作:
s.find(args) 在s中查找args的第一次出现
s.rfind(args) 在s中查找args的最后一次出现
s.find_first_of(args) 在s中查找args的任意字符的第一次出现
s.find_last_of(args) 在s中查找args的任意字符的最后一次出现
s.find_first_not_of(args) 在s中查找第一个不属于args的字符
s.find_last_not_of(args) 在s中查找最后一个不属于args的字符
25、map类型:map是键-值对的集合。map类型通常可理解为关联数组:可使用键作为下标来获取一个值,正如内置数组类型一样。而关联的本质在于元素的值与某个特定的键相关联,而非通过元素在数组中的位置来获取。
26、使用下标访问map与使用下标访问数组或vector的行为截然不同:用下标访问不存在的元素将导致map容器中添加 一个新的元素,它的键即为下标值。
27、 map::insert的使用
word_count.insert(map
word_count.insert(make_pair("Anna", 1));
或使用typedef
typedef map
word_count.insert(valType("Anna",1));
28、map对象的迭代遍历
map
while (map_it != word_count.end()){
//process
++map_it;
}
29、泛型算法:写容器元素的算法--》写入输入序列的元素
fill(vec.begin(), vec.end(), 0); //reset each elements to 0
fill(vec.begin(), vec.begin() + vec.size()/2, 10); //set subsequence of the range to 10
30、将关键字const加在形参表之后,就可以将成员函数声明为常量:
double avg_price() const; const成员不能改变其所操作的对象的数据成员。const必须同时出现在声明和定义中,如只出现在其中一处,就会出现一个编译时错误。
31、数据抽象和封装:
数据抽象是 一种依赖于接口和实现分离的编程技术。封装是一项将低层次的元素组合起来形成新的、高层次实体的技术。函数是封装的一种形式:函数所执行的细节行为被封装在函数本身这个更大的实体中。
32、数据抽象和封装的两个好处:
①避免类内部出现无意的、可能破坏对象状态的用户级错误。
②随时间推移可以根据需要改变或缺陷报告来完善和实现类的实现,而无须改变用户级代码。
33、在一个给定的源文件中,一个类只能被定义一次。定义之前声明,称为前向声明,定义之前类是一个不完全类型。
34、为什么类的定义以分号结束:因为在类的定义之后可以接一个对象定义列表。
35、何时使用this指针:当我们需要将一个对象作为整体引用而不是对象的一个成员时。最常见的情况是在这样的函数中使用this:该函数返回时对调用该函数的对象的引用。
36、不能从const成员函数返回指向对象的普通引用。const成员函数只能返回*this作为一个const引用。
37、可变数据成员:我们希望类的数据成员(甚至在const成员函数内)可以修改。这可以通过将他们声明为mutable来实现。
可变数据成员永远不能为const,甚至当它是const对象的成员时也如此。
38、类作用域:每个类都定义了自己的新作用域和唯一的类型。即使两个类具有完全相同的成员列表。他们也是不同的类型。每个类的成员不同于任何其他类的成员。
39、类定义实际上是在两个阶段中处理:
①首先编译成员声明。
②只有在所有的成员出现之后,才编译他们的定义本身。
40、构造函数:构造函数是特殊的成员函数,只要创建类类型的新对象,都要执行构造函数。
构造函数不能声明为const,const构造函数是不必要的。创建类类型的const对象时,运行一个普通构造函数来初始化该const对象。构造函数的工作是初始化对象。不管对象是否是const,都用一个构造函数来初始化。
41、构造函数分两个阶段执行:1)初始阶段;2)普通的计算阶段
不管成员是否在构造函数初始化列表中显示初始化,类类型的数据成员总是在初始化阶段初始化。初始化发生在计算阶段开始之前。
42、有些成员必须在构造函数初始化列表中进行初始化。对于这样的成员,在构造函数函数体中队他们赋值不起作用。没有默认构造函数的类类型的成员,以及const或引用类型的成员,不管是哪种类型,都必须在构造函数初始化列表中进行初始化。
可以初始化const对象和引用类型的对象,但是不能对他们赋值。
43、成员初始化的次序常常是无关紧要的。
44、只有一个类没有定义构造函数时,编译器才会自动生成一个默认的构造函数。
45、抑制由构造函数定义的隐式转换:可以通过将构造函数声明为explicit,来防止在需要隐式转换的上下文使用构造函数。
通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为explicit。将构造函数设置为explicit可以避免错误,并且当转换有用时,用户可以显示地构造对象。
46、显示初始化类类型对象的成员有三个重要的缺点:
1)要求类的全体数据成员都是public。
2)将初始化每个对象的负担放在程序员身上。
3)如果增加或删除一个成员,必须找到所有的初始化并正确更新。
47、友元机制允许一个类将对非共有成员的访问权授予指定的函数或类。通常将友元声明成组放在类定义的开始或结尾是个好主意。
48、使用类的static成员的优点:
1)static成员的名字是在类的作用域中,因此可以避免与其他类的成员或全局对象名字冲突。
2)可以实施封装。static成员可以是私有成员,而全局对象不可以。
3)通过阅读程序容易看出static成员是与特定类关联的。
49、static成员是类的组成部分但不是任何对象的组成部分,因此,static成员函数没有this指针。
因为static成员不是任何对象的组成部分,所以static成员函数不能被声明为const。
50、static数据成员必须在类定义体的外部定义(正好一次)。不像普通数据成员,static成员不是通过类构造函数进行初始化,而是应该在定义时进行初始化。
static成员独立于任何对象的存在,不是类类型对象的组成部分。
51、static数据成员可用作默认参数,非static数据成员不能用作默认参数,因为它的值不能独立于所属的对象而使用。