题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题。

接  C++ Primer学习笔记(一)

 
27、与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法直接复制和赋值(Wrong:int arr2[]=arr1;),而且程序员无法知道一个给定数组的长度---没有size操作(但可以间接获取)。
    只有当性能测试表明使用 vector 无法达到必要的速度要求时,才使用数组。
 
    
    没有所有元素都是引用的数组。 
 
    数组的维数必须用值大于等于 1 的常量表达式定义。此常量表达式只能包含整型字面值常量、枚举常量或者用常量表达式初始化的整型 const 对象。非 const 变量以及要到运行阶段才知道其值的 const变量都不能用于定义数组的维数。
 
    字符数组既可以用一组由花括号括起来、逗号隔开的字符字面值进行初始化,也可以用一个字符串字面值进行初始化。然而,要注意这两种初始化形式并不完全相同,字符串字面值包含一个额外的空字符(null)用于结束字符串。
    注意,如果不使用自字面值常量,那就要给字符数组手动提供结束符'\0',否则会溢出。

  1. #include <iostream>
  2. #include <string>
  3.  
  4. using namespace std;
  5.  
  6. //char array以'\0'为结束标记,需要手动提供!!!
  7. int main(){
  8.  
  9. int is[]={,,,,};
  10. cout<<is<<endl;
  11.  
  12. // char cs[5]={'a','b','c','d','e','\0'}; //error
  13. // char cs[5]={'a','b','c','d','e'}; //error
  14. char cs[]={'a','b','c','d','\0'}; //ok
  15. cout<<cs<<endl;
  16.  
  17. return ;
  18. }

 
28、指针,格式:string *p=&str;
     & 符号是取地址操作符,当此操作符用于一个对象上时,返回的是该对象的存储地址。
     string* ps1, ps2; // ps1 is a pointer to string, ps2 is a string(不要这样定义)
    
  一个有效的指针必然是以下三种状态之一:保存一个特定对象的地址;指向某个对象后面的另一对象;或者是 0 值。若指针保存 0 值,表明它不指向任何对象。
  未初始化的指针是无效的,直到给该指针赋值后,才可使用它。

  1. int ival = ;
  2. int *pi = ; // pi initialized to address no object
  3. int *pi2 = & ival; // pi2 initialized to address of ival
  4. int *pi3; // ok, but dangerous, pi3 is uninitialized
  5. pi = pi2; // pi and pi2 address the same object, e.g. ival
  6. pi2 = ; // pi2 now addresses no object

C++ 语言无法检测指针是否未被初始化,也无法区分有效地址和由指针分配到的存储空间中存放的二进制位形成的地址。

 
    除了使用数值 0 或在编译时值为 0 的 const 量外,还可以使用 C++ 语言从 C 语言中继承下来的预处理器变量 NULL,该变量在 cstdlib 头文件中定义,其值为 0。
    预处理器变量不是在 std 命名空间中定义的,因此其名字应为 NULL,而非 std::NULL。
 
    C++ 提供了一种特殊的指针类型 void*,它可以保存任何类型对象的地址。
    void* 表明该指针与一地址值相关,但不清楚存储在此地址上的对象的类型。 
    void* 指针只支持几种有限的操作:与另一个指针进行比较;向函数传递 void* 指针或从函数返回 void* 指针;给另一个 void* 指针赋值。不允许使用 void* 指针操纵它所指向的对象。
    
    指针提供间接操纵其所指对象的功能。与对迭代器进行解引用操作一样,对指针进行解引用可访问它所指的对象,* 操作符(解引用操作符)将获取指针所指的对象。
 
关于指针和引用:
 
  1. #include <iostream>
  2. #include <string>
  3.  
  4. using namespace std;
  5.  
  6. //练习ref和pointer
  7. int main(){
  8. string str="hello world!";
  9. string &ref=str;
  10. string *p1=&str;
  11. string *p2=&ref;
  12.  
  13. //引用和原变量指向同一个对象
  14. cout<<p1<<endl;
  15. cout<<p2<<endl;
  16.  
  17. //修改引用的内容,不是重定向,而是修改!!!
  18. ref="what\'s this";
  19. p2=&ref;
  20. cout<<p2<<endl;
  21.  
  22. return ;
  23. }
 
    引用一经初始化,就始终指向某个特定地址的对象,该引用本身不可修改!所以引用必须初始化。
    指向指针的指针:C++ 使用 ** 操作符指派一个指针指向另一指针。string **ppstr=&pstr;
 
    C++ 语言中,指针和数组密切相关。特别是在表达式中使用数组名时,该数组名字会自动转换为指向数组第一个元素的指针。    
    如果希望使指针指向数组中的另一个元素,则可使用下标操作符给某个元素定位,然后用取地址操作符 & 获取该元素的存储地址。
    与其使用下标操作,倒不如通过指针的算术操作来获取指定内容的存储地址。
  1. int ia[] = {,,,,};
  2. int *ip = ia; // ok: ip points to ia[0]
  3. int *ip2 = ip + ; // ok: ip2 points to ia[4], the last element in ia
    C++ 允许计算数组或对象的超出末端的地址,但不允许对此地址进行解引用操作。所以,可以作为类似vector.end()的作用来使用。
 
    指向const对象的指针  和   const指针:
    前者是:const string *p;    不能通过p修改指向的对象内容。
    后者是:string *const p;    不能修改p本身的值。
    如果指向 const 对象,则不允许用指针来改变其所指的 const 值。为了保证这个特性,C++ 语言强制要求指向 const 对象的指针也必须具有 const 特性
    同样,不能使用 void* 指针保存 const 对象的地址,而必须使用 const void *类型的指针保存 const 对象的地址。
    本质上来说,由于没有方法分辩 cosnt type *ptr 所指的对象是否为 const,系统会把它所指的所有对象都视为 const。 
    在实际的程序中,指向 const 的指针常用作函数的形参。将形参定义为指向 const 的指针,以此确保传递给函数的实际对象在函数中不因为形参而被修改。
 
    const string s1 和 string const s2 是一样的。
  1. string str="xxx"; //字符串字面值常量
  2. typedef string *pstr;
  3. const pstr p=&str; //这里的const pstr p和 pstr const p一样,都是将p 设为const,而非指向const对象。务必注意这点!!!
  1. #include <iostream>
  2. #include <string>
  3.  
  4. using namespace std;
  5.  
  6. int main(){
  7. int i = -;
  8. int *p1 = &i;
  9. const int *p2 = &i;
  10. int *const p3 = &i;
  11. const int *const p4 = &i;
  12.  
  13. const int i2 = i;
  14. const int *p5 = &i2;
  15. // int *const p6 = &i2;//[Error] invalid conversion from 'const int*' to 'int*' [-fpermissive] 。
  16. // 指向const对象的指针,必须[const type *p]格式 。
  17.  
  18. const int *const p7 = &i2;
  19.  
  20. return ;
  21. }
 
 
29、字符串字面值的类型,就是const char类型的数组,而且是C风格的(null-terminated)。
    C 风格字符串既不能确切地归结为 C 语言的类型,也不能归结为 C++ 语言的类型,而是以空字符 null 结束的字符数组。
 
    操作C风格字符串:cstring库文件。
    传递给其函数的指针必须具有非0值,且指向以null结束的字符数组中的第一个元素。
   
 
    C++ 语言提供普通的关系操作符实现标准库类型 string 的对象的比较。这些操作符也可用于比较指向 C 风格字符串的指针,但效果却很不相同:实际上,此时比较的是指针上存放的地址值,而并非它们所指向的字符串。if (cp1 < cp2)
 
    字符串的比较和比较结果的解释都须使用标准库函数 strcmp() 进行: 
  1. const char *cp1 = "A string example";
  2. const char *cp2 = "A different string";
  3. int i = strcmp(cp1, cp2); // i is positive
  4. i = strcmp(cp2, cp1); // i is negative
  5. i = strcmp(cp1, cp1); // i is zero
  1. 标准库函数 strcmp 3 种可能的返回值:
  2.   若两个字符串相,则返回 0 值;
  3.   若第一个字符串大于第二个字符串,则返回正数,否则返回负数。
    对大部分的应用而言,使用标准库类型 string,除了增强安全性外,效率也提高了,因此应该尽量避免使用 C 风格字符串。
 
30、与数组变量不同,动态分配的数组将一直存在,直到程序显式释放它为止。 
  每一个程序在执行时都占用一块可用的内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区或堆。
  C 语言程序使用一对标准库函数 malloc 和 free 在自由存储区中分配存储空间,而 C++ 语言则使用 new和 delete表达式实现相同的功能。 
 
  动态数组定义:int *pia = new int[10];
  在自由存储区中创建的数组对象是没有名字的,程序员只能通过其地址间接地访问堆中的对象。
  动态分配数组时,如果数组元素具有类类型,将使用该类的默认构造函数实现初始化;如果数组元素是内置类型,则无初始化。
  1. // 动态数组,内置类型,无初始化
  2. int *p=new int[];
  3. while(*p){
  4. cout<<*p<<endl;//未初始化,所以数值是乱的
  5. p++;
  6. }

也可使用跟在数组长度后面的一对空圆括号,对数组元素做值初始化:int *pia = new int[10]();

    圆括号要求编译器对数组做值初始化
 
     如果我们在自由存储区中创建的数组存储了内置类型的 const 对象,则必须为这个数组提供初始化:因为数组元素都是 const 对象,无法赋值。
    已创建的常量元素不允许修改——因此这样的数组实际上用处不大。
 
    C++ 虽然不允许定义长度为 0 的数组变量,但明确指出,调用 new 动态创建长度为 0 的数组是合法的。
    用 new 动态创建长度为 0 的数组时,new 返回有效的非零指针。该指针与 new 返回的其他指针不同,不能进行解引用操作,因为它毕竟没有指向任何元素。
 
    动态分配的内存最后必须进行释放,否则,内存最终将会逐渐耗尽。
    delete [] pia;   // []必不可少,说明是数组,而非单个对象。
 
    说明new和delete也可以创建和删除一般对象,如 int *p=new int(1024); 只是,只能返回地址,没有名字!!
 
    通常是因为在编译时无法知道数组的维数,所以才需要动态创建该数组。
 
    由于 C 风格字符串与字符串字面值具有相同的数据类型,而且都是以空字符 null 结束,因此可以把 C 风格字符串用在任何可以使用字符串字面值的地方。
    但是无法在要求C 风格字符串的地方使用string对象。例如无法使用string对象初始化字符指针。 
  1. string s1("hehe"); // 可以使用字符串字面值常量初始化string
  2. char *p = "hehe"; //OK
  3. char *p = &s1; // Error,无法使用string对象初始化字符指针!

上面可以使用string的方法来返回一个指针。

  1. const char *p = s1.c_str();
  2. c_str 函数返回 C 风格字符串,其字面意思是:“返回 C 风格字符串的表示方法”,即返回指向字符数组首地址的指针,该数组存放了与 string 对象相同的内容,并且以结束符 null 结束。
  3. c_str 返回的数组并不保证一定是有效的,接下来对 s1 的操作有可能会改变 s1 的值,使刚才返回的数组失效。如果程序需要持续访问该数据,则应该复制 c_str 函数返回的数组。

31、 C++ 允许使用数组初始化 vector 对象:

  1. const size_t arr_size = ;
  2. int int_arr[arr_size] = {, , , , , };
  3. // ivec has 6 elements: each a copy of the corresponding element in int_arr
  4. vector<int> ivec(int_arr, int_arr + arr_size); // 相当于给定首、尾指针,含头不含尾!
  1. const size_t size = ;
  2. int arr[size] = {,,,,};
  3.  
  4. vector<int> ivec(arr, arr+size);
  5. // cout<<ivec<<endl;
  6. for(vector<int>::iterator iter=ivec.begin();iter!=ivec.end();iter++){
  7. cout<<*iter<<endl;
  8. }
  9.  
  10. int *p = arr;
  11. cout<<*p<<endl;
  12.  
  13. int *p1 = (arr+); // 帅呆了
  14. cout<<*p1<<endl;
  1. //测试:相当于给定首、尾指针,含头不含尾!
  2. int *p2; // 未初始化,注意
  3. vector<int> ivec2(p2, p2+);
  4. for(vector<int>::iterator iter=ivec2.begin();iter!=ivec2.end();iter++){
  5. cout<<*iter<<endl;
  6. }
  C 程序把指向以空字符结束的字符数组的指针视为字符串。
  使用其他类型定义的类型。数组、指针和引用都是复合类型。

  C++的&&和||是短路求值。short-circut evaluation。(&和|则是位运算符)

  逻辑与和逻辑或操作符总是先计算其左操作数,然后再计算其右操作数。只有在仅靠左操作数的值无法确定该逻辑表达式的结果时,才会求解其右操作数。
 
前置操作(++i)需要做的工作更少,只需加 1 后返回加 1 后的结果即可。而后置操作符则必须先保存操作数原来的值,以便返回未加 1 之前的值作为操作的结果。
对于 int 型对象和指针,编译器可优化掉这项额外工作。但是对于更多的复杂迭代器类型,这种额外工作可能会花费更大的代价。
因此,养成使用前置操作这个好习惯,就不必操心性能差异的问题。 
 
箭头操作符(->):(*p).function()  等价于  p->function()。
  1. #include <iostream>
  2. #include <string>
  3. #include <vector>
  4.  
  5. using namespace std;
  6.  
  7. // string * 的vector,打印string。
  8. int main(){
  9. string s1="hehe";
  10. string s2="haha";
  11. string s3="what";
  12. string s4="abc";
  13. string s5="world";
  14.  
  15. vector<string*> pvec;
  16. pvec.push_back(&s1);
  17. pvec.push_back(&s2);
  18. pvec.push_back(&s3);
  19. pvec.push_back(&s4);
  20. pvec.push_back(&s5);
  21.  
  22. for(vector<string*>::iterator iter=pvec.begin();iter!=pvec.end();++iter){
  23. cout<<*iter<<"\t"<<**iter<<"\t"<<(*iter)->size()<<endl;
  24. }
  25.  
  26. return ;
  27. }
  1. sizeof返回的类型是size_t
  2.  
  3. sizeof (type name);
  4. sizeof (expr);
  5. sizeof expr;
  6.  
  7. sizeof 用于 expr 时,并没有计算表达式 expr 的值。特别是在 sizeof *p 中,指针 p 可以持有一个无效地址,因为不需要对 p 做解引用操作。
  8.  
  9. char 类型或值为 char 类型的表达式做 sizeof 操作保证得
  10. 对引用类型做 sizeof 操作将返回存放此引用类型对象所需的内在空间大小。
  11. 对指针做 sizeof 操作将返回存放指针所需的内在大小;注意,如果要获取该指针所指向对象的大小,则必须对指针进行引用。
  12. 对数组做 sizeof 操作等效于将对其元素类型做 sizeof 操作的结果乘上数组元素的个数。
  13.  
  14. 因为 sizeof 返回整个数组在内存中的存储长度,所以用 sizeof 数组的结果除以 sizeof 其元素类型的结果,即可求出数组元素的个数。
 逗号表达式是一组由逗号分隔的表达式,这些表达式从左向右计算。逗号表达式的结果是其最右边表达式的值。如果最右边的操作数是左值,则逗号表达式的值也是左值。
此类表达式通常用于 for 循环。
 
 删除指针后,该指针变成悬垂指针。悬垂指针指向曾经存放对象的内存,但该对象已经不再存在了。悬垂指针往往导致程序错误,而且很难检测出来。 
一旦删除了指针所指向的对象,立即将指针置为 0,这样就非常清楚地表明指针不再指向任何对象。 
 
  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. int main(){
  6. int *pi=new int[]; //动态创建对象,只能返回地址
  7. delete [] pi;
  8.  
  9. int *p = new int;
  10. delete p;
  11. p=; //防止悬垂指针
  12.  
  13. string *pstr=new string(,'');
  14. cout<<*pstr<<endl;
  15. delete pstr;
  16. pstr=; //防止悬垂指针
  17.  
  18. return ;
  19. }
隐式转换:C++ 定义了算术类型之间的内置转换以尽可能防止精度损失。通常,如果表达式的操作数分别为整型和浮点型,则整型的操作数被转换为浮点型。
 
算数转换:在包含多种类型的表达式中,转换规则要确保计算值的精度。
 
显式转换也称为强制类型转换(cast),包括以下列名字命名的强制类型转换操作符:static_cast、dynamic_cast、const_cast和 reinterpret_cast。
例子:ival *= static_cast<int>(dval); // converts dval to int 
 
命名的强制类型转换符号的一般形式如下: 
    cast-name<type>(expression); 
其中 cast-name 为 static_cast、dynamic_cast、const_cast 和 reinterpret_cast 之一,type 为转换的目标类型,而 expression 则是被强制转换的值。
 
    dynamic_cast 支持运行时识别指针或引用所指向的对象。
    const_cast ,顾名思义,将转换掉表达式的 const 性质。
    编译器隐式执行的任何类型转换都可以由 static_cast 显式完成。
  1. 如果编译器不提供自动转换,使用 static_cast 来执行类型转换也是很有用的。例如,下面的程序使用 static_cast 找回存放在 void* 指针中的值:
  2. void* p = &d; // ok: address of any data object can be stored in a void*
  3. double *dp = static_cast<double*>(p); // ok: converts void* back to the original pointer type
建议:避免使用强制类型转换。
 
旧式强制类型转换(略)
    旧式强制转换符号有下列两种形式: 
        type (expr); // Function-style cast notation 
        (type) expr; // C-language-style cast notation 
 
 
 
 
 
 

C++ Primer学习笔记(二)的更多相关文章

  1. C++ Primer学习笔记二

    vector<int> a(10,0); for(vector<int>::iterator itor=a.begin();itor!=a.end();itor++) *ito ...

  2. C++Primer学习笔记(二、基础)

    1.两种初始化方式,直接初始化语法更灵活,且效率更高. ); // 直接初始化 direct-initialization ; // 赋值初始化 copy-initialization 2.const ...

  3. C++ Primer学习笔记(三) C++中函数是一种类型!!!

    C++中函数是一种类型!C++中函数是一种类型!C++中函数是一种类型! 函数名就是变量!函数名就是变量!函数名就是变量! (---20160618最新消息,函数名不是变量名...囧) (---201 ...

  4. WPF的Binding学习笔记(二)

    原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...

  5. AJax 学习笔记二(onreadystatechange的作用)

    AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...

  6. [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计

    源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...

  7. JMX学习笔记(二)-Notification

    Notification通知,也可理解为消息,有通知,必然有发送通知的广播,JMX这里采用了一种订阅的方式,类似于观察者模式,注册一个观察者到广播里,当有通知时,广播通过调用观察者,逐一通知. 这里写 ...

  8. java之jvm学习笔记二(类装载器的体系结构)

    java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...

  9. Java IO学习笔记二

    Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...

随机推荐

  1. APP三种开发模式

    目前主流应用程序大体分为三种:Web App(网页应用).Hybrid Ap(混合应用)p. Native App(原生应用). nativeapp是一个原生程序,一般运行在机器操作系统上,有很强的交 ...

  2. mysql 查看是否被锁

    SHOW OPEN TABLES FROM huahua

  3. mysql 表锁——读锁和写锁

    注意, 0.表的索引类型必须是InnoDB.相关链接:http://www.cnblogs.com/CyLee/p/5579672.html 1.如果你使用Navicat Premium,有可能会出现 ...

  4. Linux安装mysql——源码安装

    1.假设已经有mysql-5.5.10.tar.gz以及cmake-2.8.4.tar.gz两个源文件 (1)先安装cmake(mysql5.5以后是通过cmake来编译的) [root@ rhel5 ...

  5. object-c 获得目录(包括子目录)下所有文件和文件夹路径

    void getAllPathNameInDirectory(vector<string>&filePathList,vector<string>&direct ...

  6. mybatis 一二事(3) - 多表关联查询

    db.properties jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/order jdbc.user ...

  7. 微信wap开发---页面自适应大小

    <meta name="viewport" content="width=device-width, initial-scale=0.5, minimum-scal ...

  8. Ubuntu12.04下tomcat的安装与配置

    1.下载tomcat 我的tomcat是从 http://tomcat.apache.org/download-70.cgi 这里下载的tar.gz版本的. 2.解压tomcat $sudo tar ...

  9. js修改input的type属性问题(兼容所有浏览器,主要用于密码类的默认有提示文字的效果)

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...

  10. java的regex问题笔记

    参考javadoc java.util.regex.Pattern 里面有一些说明,如果还有不明白的地方 yes,google it. @ “不能以0开头,1到多位数字,字符集为0到9” " ...