文章目录
C和C++的区别
设计思想上
- C++是面向对象的语言,而C是面向过程的结构化编程语言
语法上
- C++具有封装、继承和多态三种特性
- C++相比C,增加多许多类型安全的功能,比如强制类型转换、
- C++支持范式编程,比如模板类、函数模板等
static
c/c++共有
全局变量
- 表明一个全局变量只对定义在同一文件中的函数可见。
局部变量
- 表明该变量的值不会因为函数终止而丢失。但作用域也还是局部。
函数
- 表明该函数只在同一文件中调用。
因为默认都是extern的
c++独有
类的成员
- 对多个对象来说,静态数据成员只存储一处,供所有对象共用
类的函数。
- 它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。
- 不能直接引用类中说明的非静态成员,可通过对象来引用,格式:
<类名>::<静态成员函数名>(<参数表>);
内存区
栈区
- 栈区(stack)由编译器自动分配并释放,存放的是函数的参数值,局部变量等,方法调用的实参也是保存在栈区的。优点是快速高效,缺点是有限制,数据不灵活。
堆区
- 由程序员分配和释放,如果不释放,可能会出现内存泄露,优点是灵活方便,缺点是效率降低,不同堆分配的内存无法互相操作。
静态区
- 存全局变量和静态变量,自动释放。又分为未初始化和初始化为非0的:bss段,data段。为什么? 全零节省磁盘空间。
文字常量区
- 放常亮字符串,自动释放
代码常量区
- 存放函数的二进制代码
内存泄漏
没有匹配地调用new和delete
- 在堆里创建了对象占用了内存,但是没有显示地释放对象占用的内存;
- 在类的构造函数中动态的分配了内存,但是在析构函数中没有释放内存或者没有正确的释放内存
- 没有正确地清除嵌套的对象指针
delete中没有使用方括号
- 方括号是告诉编译器这个指针指向的是一个对象数组,否则只清理一个,数字填大了奔溃,小了泄漏
- char* c = new char[10],delete c即可,释放定义了析构函数的对象数组需要方括号[]最好就是直接delete []p
指向对象的指针数组不等同于对象数组
- 释放指针数组时,不仅要释放每个对象的空间,还要释放每个指针的空间,delete []p只是释放了每个指针,正确的做法,是通过一个循环,将每个对象释放了,然后再把指针释放了。
缺少拷贝构造函数
- 没定义拷贝函数时,调用默认的,浅拷贝,释放会释放2次同一个堆,崩溃
缺少重载赋值运算符
- a.data = b.data。a之前data的没释放
返回局部的指针或引用
- 返回的是一个空引用或者空指针,因此变成野指针,应该不返回引用
没有将基类的析构函数定义为虚函数
- 当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确是释放,因此造成内存泄露
c++中四种cast转换
static_cast
- 和c的强制转换差不多,主要用于基本数据类型的转换,子类指针转换成父类指针的转换
dynamic_cast
- 动态类型转换,运行时类型安全检查(转换失败返回NULL):
- 必须要有虚函数
- T必须是类的指针、类的引用或者void *
- dynamic_cast主要用于类层次间的上行转换和下行转换,类之间的交叉转换。
reinterpret_cast
- 重新解释,用的少
const_cast
- 加减const
指针和引用
- 指针是吧要指向的数的地址给一个指针,通过*来访问改变其值,引用就只是起别名
- 可以有多级指针,但只能一级引用
智能指针
auto_ptr
- 可以避免忘记写delete的情况,但当两个只能指针指向一个时存在潜在的内存崩溃问题。C++11弃用
unique_ptr
- 独占模式,一个指针只能用于一个对象
shared_ptr
- 在使用引用计数的机制上提供了可以共享所有权的智能指针
weak_ptr
- weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。
位运算
与&
求1的个数
- 循环while(x>0)
- 按位与1, 如果是结果为1, 那么计数就加 1,x >>= 1
- x = x & (x - 1),每次消去一个1,直到所有1消除完
清零或取某几位
与1与判断是否为2的倍数
虚函数
实现原理
- 虚函数在运行的时候动态绑定
- 通过虚函数表来实现,表的最前面存了虚函数地址,子函数实例化时会更新虚函数表中的值
- Base *b = new Derive();b->f();通过父类指针指向子类地址实现多态。
注意事项
- 任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法,它本身还是个父类指针,根本没子类专有的东西
- 通过虚函数表可以访问子类函数以及父类保护的函数pFun = (Fun)((void)(void)(&b))
- 父类析构设为虚函数,否则析构父类指针不会析构子类。
纯虚函数
- 在后面加上=0,一般来说纯虚函数父类不写默认,子类必须实现,而一般的虚函数父类必须写默认,子类可以实现。
- 纯虚函数不实例化,通过子类实例化
构造与析构
基本顺序问题
- 构造顺序:先生爷爷,在生父亲,再生哥哥(成员),最后生你
- 析构顺序:先打败你,再打败你哥哥,再打败你爸爸,再打败你爷爷
函数指针
作用
- 把函数的返回值用指针包起来用
用法
- 函数原型:T fun(T2){}
- 创建个函数指针:T (*p)(T2);
- 赋值:p = fun;
- 使用:(*fun)(T2);
typedef用法
- 函数原型:T fun(T2){}
- typedef个函数指针:typedef T (*P)(T2);
- 赋值:P p = fun;
- 使用:fun(T2);
fork,exec,wait
fork() 函数
- 影分身出一个新进程,新进程和原进程一样的内存
- 事实上多进程技术,不建议和多线程一起使用
wait() 函数
- 加上后主线程等子线程exit后再执行
exec() 函数
- 启动一个可执行文件,之后脱离父进程,不共享了
fork文件操作
- fork是拷贝值,包括缓冲区的值
- fwrite是带缓冲区的,而write是不带缓冲区的,它可以实时写入。
信号量,临界区,互斥,自旋锁
互斥锁
- 作用就是互斥,mutual exclusive,是用来保护临界区(critical section)的。所谓临界区就是代码的一个区间,如果两个线程同时执行就有可能出问题,所以需要互斥锁来保护。
信号量(semaphore)
- 是一种更高级的同步机制,mutex可以说是semaphore在仅取值0/1时的特例。Semaphore可以有更多的取值空间,用来实现更加复杂的同步,而不单单是线程间互斥。
自旋锁
- 是一种互斥锁的实现方式而已,相比一般的互斥锁会在等待期间放弃cpu,自旋锁(spinlock)则是不断循环并测试锁的状态,这样就一直占着cpu。
函数参数有几种形式以及区别
按值传递
- 不对实参进行操作
地址传递
- 把实参的地址传送给的形参,从而使得形参指针和实参指针指向同一个地址。因此,被调用函数中对形参指针所指向的地址中内容的任何改变都会影响到实参。
引用传递
- 以引用为参数,则既可以使得对形参的任何操作都能改变相应的数据
进程通信方式和线程通信方式
进程通信(7种)
- 管道( pipe )
- 管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。。
- 有名管道 (namedpipe)
- 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
- 信号量(semophore )
- 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制
- 消息队列( messagequeue ) :
- 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
- 信号 (sinal )
- 信号用于通知接收进程某个事件已经发生。
- 共享内存(shared memory )
- 共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。
- 套接字(socket )
- 套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同设备及其间的进程通信。
线程通信(3种)
- 锁机制
- 信号量机制
- 信号机制