首页 > 编程语言 > 详细

C++ 基础部分

时间:2020-04-02 18:40:02      阅读:65      评论:0      收藏:0      [点我收藏+]

1.迭代器类型

迭代器是一种检查容器内元素并遍历元素的数据类型。C++更趋向于使用迭代器而不是下标操作,因为标准库为每一种标准容器(如vector)定义了一种迭代器类型,而只用少数容器(如vector)支持下标操作访问容器元素。

共有五类迭代器:Input Iterator ,Output Iterator,Forwaed Iterator,Bidirectional Iterator,Random Access Iterator.

  • 输入迭代器(Input Iterator):通过对输入迭代器解除引用,它将引用对象,而对象可能位于集合中。最严格的输入迭代只能以只读方式访问对象。例如:istream。
  • 输出迭代器(Output Iterator):该类迭代器和Input Iterator极其相似,也只能单步向前迭代元素,不同的是该类迭代器对元素只有写的权力。例如:ostream, inserter。
    以上两种基本迭代器可进一步分为三类:
  • 前向迭代器(Forward Iterator):该类迭代器可以在一个正确的区间中进行读写操作,它拥有Input Iterator的所有特性,和Output Iterator的部分特性,以及单步向前迭代元素的能力。
  • 双向迭代器(Bidirectional Iterator):该类迭代器是在Forward Iterator的基础上提供了单步向后迭代元素的能力。例如:list, set, multiset, map, multimap。
  • 随机迭代器(Random Access Iterator):该类迭代器能完成上面所有迭代器的工作,它自己独有的特性就是可以像指针那样进行算术计算,而不是仅仅只有单步向前或向后迭代。例如:vector, deque, string, array。

STL迭代器,是一个类模板,模拟了指针的功能,对很多指针常用的运算符进行了重载,封装了原生的指针,提供更高级的行为。返回的是对象的引用而不是值,需要解引用才可以输出。

2.虚函数

虚函数是实现多态的一种方式。在多态中有函数重载的方法(编译器决定、静态多态),也有函数重写(运行期决定,动态多态),实现这种动态多态,就是依靠虚函数的方式,具体的说就是借助指针或者是引用,在基类中的函数必须有virtual关键字。我们使用基类的指针去指向派生类(子类对象),可以统一的用父类指针去表示各子类对象,只有当运行期间的时候,我们才知道使用的是哪个版本的函数,这就是虚函数的作用。
虚函数的实现方式是利用虚指针以及虚函数表来实现。(具体见继承篇:单继承、多继承、菱形继承)

  • 每个类生成一个虚表(virtual table,vtbl),虚表中存放一堆指针,指向该类的每一个虚函数,地址将按照声明顺序排列

    虚表属于类,不属于具体的对象。一个类只有一份虚表。为了让每个包含虚表的类的对象都拥有一个虚表指针,编译器在类中添加了一个指针,*__vptr,用来指向虚表。这样,当类的对象在创建时便拥有了这个指针,且这个指针的值会自动被设置为指向类的虚表。

  • 每一个类对象,都拥有一个虚表指针(vptr),由编译器生成。虚表指针的设定和重置,都有类的复制控制(也就是构造,析构,赋值操作符)完成。。现在许多编译器,把vptr放在一个类对象的最前端。

虚析构

父类的析构函数设置为虚函数是为了防止内存泄漏,当我们使用父类的指针指向子类的对象时,希望释放基类指针的时候,可以释放掉子类的空间。
如果我们不设置父类的析构函数设置为虚函数,当父类指针指向子类时,释放父类指针,并不会调用子类的析构函数(在子类的析构函数中几乎不可避免的涉及到子类资源的释放,比如有一个指针指向了一块内存,不析构就内存泄漏了),只会调用基类的析构函数。
而不一定每一个类都有派生类都要被继承,所以默认构造并不是虚析构。

3.指针和引用

指针有一块自己的空间,本身就是一个对象,允许对指针去赋值和拷贝,指针也可以为空。
而引用是给对象起了一个别名,写成&d符号,引用必须要去初始化,之后会绑定在一起不可以修改,所以不可以为空。

他们的本质区别在于 指针指向一块内存,指针的内容就是内存的地址,而引用是某块内存的别名,无法改变指向

一些不同点:

  • 运算符的意义不一样,对于指针来说,++表现的地址的变化,而引用的++,是对对像本身的++操作。
  • 函数传递传指针和引用形式:指针传递本身是一种值传递,传递一个地址值。被调用的函数把形参当做局部变量,在栈中生成副本,不会影响到实参。但是引用传递不一样,同样开辟内存空间,但是存放的是主调函数存放的实参变量地址,在函数里的操作是会影响到实参的。

4.vector、map、set

vector是动态空间,size表示实际存储的数据数量,而capacity则是当前可容纳的数量,也就是开辟的内存。当vector动态增加时,一旦旧有的空间满了,就会以原来的capacity大小俩倍申请另一块空间,把内容拷贝过去,再增加新元素,最后把原空间释放。如果频繁的进行扩展,可以在一开始的时候提前声明用来开辟大容量。

无论释放(pop_back)、删除(erase)还是清空(clear),都只会改变size,不会改变capacity,只有vector析构才会清空内存。

map是关联容器,用键值对来存储,底层实现是红黑树,插入删除等,都可以再o(log n)时间内完成。
set底层也是红黑树,会自动调整二叉树结构。
关于红黑树见别的章节。

map和unordered_map

unordered_map是哈希表实现的,是无序的,时间复杂度是常数级别,但是空间复杂度很高,如果时间上要求高效率就用unoerdered_map,空间上敏感或者要求有序就用map

5.预编译

预编译其实就是在编译过程之前,进行的一些代码文本的替换工作。

  • 比如我们非常熟悉的#include,预编译做的工作其实非常简单,就是把要包含的文件,原样拷贝过来,直接粘贴到include的位置,当然,编译器会进行一些头文件保护的工作,保证不会重复包含。
  • #define 宏定义的替换
  • #if #endif 如果是0,就执行if后面的,否则就执行endif后面。

6.explicit 关键字

显式构造函数。如果构造函数声明了这个关键字,就不允许隐式自动转换。
举个例子,有一个参数的的构造函数,如果声明了explicit,就不可以用A a = 1这种方式构造,因为这其实就是一种隐式转换,把1当做参数去进行构造。

7.智能指针

https://www.cnblogs.com/EvansPudding/p/12565968.html

8.C++ cast转换

https://www.cnblogs.com/EvansPudding/p/12566210.html

9.static关键字

  • 全局静态变量,全局变量前加入static,除了声明它的文件之外,别的文件看不到。
  • 静态函数,在函数返回类型前,加上static,这个函数就智能在这个文件中使用,不会被其他文件中的同名函数冲突
  • 局部静态变量 局部变量前加入static关键字,作用域只在这个局部作用域,比如一个函数,函数结束,作用域结束,但是静态变量不会销毁,仍然在内存中,直到这个函数再次被调用,值不变。
  • 类中的静态数据成员和静态函数成员,他们都属于类的静态成员,是对象共享,不属于某一个某一个对象成员。对于静态成员函数来说,引用是不需要对象名的。

10.深拷贝浅拷贝

https://www.cnblogs.com/EvansPudding/p/12566403.html

C++ 基础部分

原文:https://www.cnblogs.com/EvansPudding/p/12621250.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!