C++ Primer Plus第6版18个重点笔记(一)

2015-01-27 06:01:15 · 作者: · 浏览: 62
?
?
首先,它能够明确指定类型,有类型检查功能。
其次,可以使用C++的作用域规则将定义限制在特定的函数或文件中。
第三,可以将const用于更复杂的类型,比如数组和结构。
?
C语言中也有const,其与C++中const的区别是:一是作用域规则不同;另一个是,在C++中可以用const值来声明数组长度。
?
?
不能简单地将整数赋给指针,如下所示:
?
?
int *ptr;
ptr = 0xB8000000; ?// type mismatch
在这里,左边是指向int的指针,因此可以把它赋给地址,但右边是一个整数。您可能知道,0xB8000000是老式计算机系统中视频内存的组合段偏移地址,但这条语句并没有告诉程序,这个数字就是一个地址。在C99标准发布之前,C语言允许这样赋值。但C++在类型一致方面的要求更严格,编译器将显示一条错误消息,通告类型不匹配。要将数字值作为地址来使用,应通过强制类型转换将数字转换为适当的地址类型:
?
?
int *ptr;
ptr = (int *) 0xB8000000; ?// type now match
这样,赋值语句的两边都是整数的地址,因此这样赋值有效。
?
注意,pt是int值的地址并不意味着pt本身的类型是int。例如,在有些平台中,int类型是个2字节值,而地址是个4字节值。
?
?
为什么说前缀++/--比后缀++/--的效率高?
?
对于内置类型和当代的编译器而言,这看似不是什么问题。然而,C++允许您针对类定义这些运算符,在这种情况下,用户这样定义前缀函数:将值加1,然后返回结果;但后缀版本首先复制一个副本,将其加1,然后将复制的副本返回。因此,对于类而言,前缀版本的效率比后缀版本高。
总之,对于内置类型,采用哪种格式不会有差别,但对于用户定义的类型,如果有用户定义的递增和递减运算符,则前缀格式的效率更高。
?
逗号运算符
到目前为止,逗号运算符最常见的用途是将两个或更多的表达式放到一个for循环表达式中。逗号运算符的特性有下面几个:
?
它确保先计算第一个表达式,然后计算第二个表达式;
i = 20, j = 2 * i; // i set to 20, then j set to 40
逗号表达式的值是第二部分的值。例如,上面表达式的值为40。
在所有运算符中,逗号运算符的优先级是最低的。例如:
cats = 17, 240;
被解释我:
(cats = 17), 240;
也就是说,将cats设置为17,后面的240不起作用。如果是cats = (17, 240);那么cats就是240了。
有用的字符函数库cctype
从C语言继承而来,老式格式是ctype.h,常用的有:
?
1.png
?
快排中中值的选取:
将元素每5个一组,分别取中值。在n/5个中值里面找到中值,作为partition的pivot。
为什么*不每3个一组?保证pivot左边右边至少3n/10个元素,这样最差O(n)。
?
C++存储方案:C++三种,C++11四种
这些方案的区别就在于数据保留在内存中的时间。
?
自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。C++有两种存储持续性为自动的变量。
静态存储持续性:在函数定义外定义的变量和使用关键字static定义的变量的存储持续性都为静态。它们在程序整个运行过程中都存在。C++有3种存储持续性为静态的变量。
线程存储持续性(C++11):当前,多核处理器很常见,这些CPU可同时处理多个执行任务。这让程序能够将计算放在可并行处理的不同线程中。如果变量是使用关键字thread_local声明的,则其生命周期与所属的线程一样长。本书不探讨并行 编程
动态存储持续性:用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止。这种内存的存储持续性为动态,有时被称为自由存储(free store)或堆(heap)。
?
自己写string类注意事项:
?
关于记录已有对象数object_count
不要在类声明(即头文件)中初始化静态成员变量,这是因为声明描述了如何分配内存,但并不分配内存。对于静态类成员,可以在类声明之外使用单独的语句来进行初始化,这是因为静态类成员是单独存储的,而不是对象组成部分。请注意,初始化语句指出了类型int(不可缺少),并使用了作用域运算符,但没有使用关键字static。
初始化是在方法文件中,而不是在类声明文件中进行的,这是因为类声明位于头文件中,可能被包含多次,这样若在头文件中进行初始化静态成员,将出现多个初始化语句副本,从而引发错误。
对于不能在类声明中初始化静态成员的一种例外情况是:静态数据成员为整型或枚举型const。即如果静态数据成员是整型或枚举型,则可以在类声明中初始化。
注意重写拷贝构造函数和赋值运算符,其中赋值运算符的原型为:
Class_name & Class_name::operator=(const Class_name &);
它接受并返回一个指向类对象的引用,目的应该是方便串联使用。
何时调用拷贝(复制)构造函数:
?
StringBad ditto (motto); ??
StringBad metoo = motto;?
StringBad also = StringBad(motto);?
StringBad * pStringBad = new StringBad (motto);
以上4中方式都将调用:StringBad(const StringBad &)
?
其中中间两种声明可能会使用复制构造函数直接创建metoo和also对象,也可能使用复制构造函数生成一个临时对象,然后将临时对象的内容赋给metoo和also,这取决于具体的实现。最后一种声明使用motto初始化一个匿名对象,并将新对象的地址赋给pStringBad指针。
每当程序生成了对象副本时,编译器都将使用复制构造函数。具体的说,当函数按值传递对象或函数返回对象时,都将使用复制构造函数。记住,按值传递意味着创建原始变量的一个副本。
编译器生成临时对象时,也将使用复制构造函数。例如,将3个Vector对象相加时,编译器可能生成临时的Vector对象来保存中间的结果。
另外,String sailor = sports;等价于String sailor = (String)sports;因此调用的是拷贝构造函数
何时调用赋值运算符:
?
将已有的对象赋给另一个对象时,将调用重载的赋值运算符。
初始化对象时,并不一定会使用赋值操作符:
StringBad metoo=knot; ? // use copy constructor, possibly assignment, too
这里,metoo是一个新创建的对象,被初始化为knot的值,因此使用赋值构