看C++的一些琐碎(第一部分2-8章)

看C++的一些琐碎(随时更新)

        之前写的从面向对象看C++系列暂时告于段落了,将的基本上是C++的一些OOP设计机制。但是,C++是一门强大的语言,不仅仅是 OOP的,还有很多其他方面的,具体的应用。在这里,记录一些我在可能《C++primer》时感觉比较好的地方,或者自己没有注意的地方,没什么逻辑。

1.标准库string类型和字符串字面值:字符串常量是存储在内存的常量区,不可以修改;

2.下标操作[]: 下标操作可以作为左值,也可以作为右值;有C语言基础的人很容易习惯用它来给vector<>添加新元素,这其实是不对的!因为下标操作仅可以已经存在的元素赋值!正确的做法应该是使用push_back()来进行元素的添加。

3.对于语句for(vector<int>:: size_type ix=0;ix!=invec.size();ix++),有没有什么想法?从我个人的第一反应就是,为什么中间的判断是!=invec.size(),而不是<;还有啊,刚开始怎么不把size()的副本先存一下,这样岂不是每次都要计算一个这个函数吗?答案:有些数据结构是动态增长的(这里vector就是),刚开始和后面也许它的size()是不一样的,这样写更合理,同时,C++中有内联函数这个概念,即每次执行的时候就替换成相应的代码,像这种小的库函数,通常都是内联的,也就不存在什么效率问题了。

4.关于迭代器iterator类型。我发现我一直不知道迭代器类型,亦如我不知道容器类的存在。这个类型是依附于容器存在的,到现在,我突然有种恍然大悟的感觉,这就是把指针给封装了啊!所以才会有迭代器的判等操作是intr.begin()!=intr.end();才会有迭代器的自增和解引用。

5.关于bitset类。这个类和vector类似,也是类模板,只不过这个变化的类型是是个数而不是对象的类型。在bitset类中,对象只是0,1这两个位。它的初始化方法有很多种,可以用unsigned long,可以用string;比如: bitset<32> bitvec3(0xffff);那么bitvec3显示的是0000,,,111,,,从低位开始,不够的用0补齐;而用string类型也是类似,比如一个string s="1100", bitset<8> bitvec4(s);那么输出就应该是00001100。


从这个小代码里面可以可看出,bitset类型是从左向右存储的,即左边是高位,右边是低位;而其他的比如string,vector等则是左边是反的。其实bitset类型更像是我们写的数字,越左,位越大。

5.1 第三章里面介绍了4个标准库:string,vector,iterator和bitset;他们都可以使用[]和.size()。其中[]表示取下标操作,可以作为左值;但是vector通常不使用[]赋值,而是使用push_back(),

6. C++中强调尽量不使用数组和指针。在C++中,推荐使用vector和iterator来替换数组和指针。vector比数组好在:可以通过size()得到大小,长度可变,可以通过push_back添加元素;此外,iterator把指针封装了,使用的时候不用担心越界。来看这样一段程序:

const int size_t arry_size=5;

int int_arr[arr_sz]={1,2,3,4,5};

//在遍历数组的时候可以这么来:

for(int *pbegin=int_arr, *pend=int_arr+zrry_size; pbegin!=pend; pbegin++) //这样就可以防止数组的越界,pend是个哨兵(因为把指针使用的很像迭代器!)

还有,C++里面的string类也是用来替换C里面的字符数组的;使用字符数组的时候,可以采用两种初始化方式:{‘1’,‘2’,‘3’}和“123”;但是这两者的长度不一样,后者等价于{‘1’,‘2’,‘3’,‘\0’}.

7.void * 类型只允许以下几种存在:作为函数的参数或者返回值;与另一个指针进行比较;绝对不允许使用void* 操纵他所指向的对象!

8.必须初始化的东西:引用和const指针。 这两个东西都必须有初始化是因为,引用是一个对象的别名,对引用变量的操作实质上都是对这个变量的操作,因此必须初始化;而const指针是,这个指针不能改变,即一旦初始化之后就不能再改了,只能指向这个对象,因此也必须初始化。

9. C风格的字符串(char *)可以使用很多函数 strlen, strcmp, strcpy等,注意他们的使用都是以\0作为结束标志的,在动态申请的时候注意+1。

10 .动态数组。 我们常说的数组是 数组变量,是个变量,他有变量名,存储在内存的栈里面; 而所谓的动态数组, 则是存放在堆/自有存储区(C里面用 malloc, free; C++里面用new delete)里面,没有名字;


11. 终于看到第五章了,这一章讲的是表达式。表达式是指:操作数通过操作符组合而成;每个表达式都有个结果,最简单的表达式可以是个字面值常量。有些表达式的值是依赖于机器的,比如,除法和取模运算,在有一个操作数是负数的时候,其运算结果是machine-dependent。
12.位操作,是应用在整形操作数上的低级操作,推荐使用 bitset。
13. 自增操作,有前置和后置之分;前置比后置运算更高效:只需要+1后返回+1的结果即可(修改后的结果直接作为表达式的结果);但是后置则需要先保存当前的值,然后再加1,因为要返回之前的值。还有自增操作的优先级高于解引用,所以,如果看到类似于*itr++的操作,就是先自增,再解引用。
13.1 看到了一个很好玩的题目: 为什么C++不叫做++C?答案也就是自增操作的优先级问题。C++说明可以向下兼容C,而++C则说明C用不了了。
14. sizeof()操作符是 用来返回一个对象或者类型名的长度的,返回值的类型是size_t。 size_t是一个machine-dependent 的unsigned类型,单位是字节,定义在cstddef头文件中。sizeof 后面可以跟 对象, 类型名, 某种类型的变量以及表达式,但是在计算表达式的时候,并没有真正计算表达式,只是计算他的返回值的类型。
15. 动态创建对象的初始化问题: 
    string *ps=new string; //ps 会自动自动调用string类的构造函数,所以ps已经初始化了,不用在string后面写()
    int *pi=new int 和 int *pc=new int() //这两个不一样,前者米有初始化,只是指向了一个整数,后面的初始化了,只像一个值为0的整数
 所以说,()只是显示的说明有初始化这个步骤。  
16.delete操作: 在使用new动态创建指针后,一定要记得释放,否则内存会泄露。但是delete也不是什么都可以释放的:只能释放new出来的指针(或者0指针);一旦被释放,他就变成了不确定的指针 dangling pointer,虽然仍然指向以前的对象,但是因为以前的对象没有了,所以就没有意义,因此他的存在也就很危险,最好是在delete 了以后,将他置为0,这样就说明他不在指向任何东西了。
17. 关于类型转换:有隐式转换(implicit type convertion)和显示转换(cast)。个人觉得隐式转换不用管,而显式转换(强制类型转换)有:dynamic_cast,const_cast(转换掉表达式的const性质),static_cast(所有的隐式转换都可以由这个完成,并且不会有警告),使用的时候类似:
double a =97.0;
char c=static_cast<char>(a);//此时,c就变成了'a'
还有个reinerpret_cast,将操作数解释为另外一种类型 (用于提供操作数为模式的重新解释)。
在引入这4种操作符之前,C++曾经使用()将强制类型转换放进去的;但是这样可视性差,难以跟踪错误。此外,应该尽量避免使用强制转换,因为他们的使用意味着设计缺陷。

18:函数由函数名和一组操作数(参数)唯一的表示,他就像是内置的操作符一样,区分他们的时候注意,编译器对于const形参和非const的形参视为一样的。
19: 关于引用传参,他有两个明显的作用:
第一是,可以让函数多返回一个值
第二是,可以提高效率,这一点主要针对参数对象很大的时候,引用传参不用复制对象,而是直接操作它;如果仅仅是为了不复制参数,那么应该将形参定义为const引用。
20:关于const引用。如果,使用引用传餐的唯一目的是 避免复制实参, 那么就应该 使用const引用。如果把它弄成普通引用的话,会不能使用右值和const对象。比如字面值常量等。
21:关于容器类型和数组类型的参数:通常,函数选择参数不会选择vector等类似的容器;因为选择容器作为参数通常意味着要赋值元素,而实际通常选择迭代器;也不会选择数组作为参数,因为数组不能复制,并且数组名作为参数传进去会被自动转换成指针

22: 如果使用数组作为参数,并且不想素组越界,有三种常用的方法:

C风格的:就是使用某个特殊符号作为结束符(比如string里面是\0)

C++风格的(标准类库,强调封装性):类似迭代器,将首尾指针封装起来

显示定义数组的长度

23:参数里面指针和引用有什么差别呢?当函数需要处理数组,且函数体不依赖数组的长度的时候;这样可以使用任意长度的数组,而引用,则只能使用长度匹配的数组才可以,限制了函数的使用,但同时也是安全的。

24:函数指针,这句话啥意思ne :

int  (*ff(int)) (int*, int )

意思是:函数ff(int)的返回值是一个指向参数为(int*, int),返回值为int 的函数

      


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值