C与C++的区别
设计思想
语法
C++与C相比,具有重载、继承和多态三种特性。
C++与C相比,增加了许多类型安全的功能,例如强制类型转换。
C++与C相比,支持了范式编程,例如模版类、函数模版等。
static关键字的作用
全局静态变量
方法:在全局变量前加上关键字static,全局变量就定义成了一个全局静态变量。
位置:位于静态储存区,整个程序运行期间都一直存在。
初始化:未经初始化的全局今天变量会被自动初始化为0(自动对象的值是任意的,除非被显式初始化)。
作用域:全局。全局静态静态变量在声明她的文件之外值不可见的,准确的说是从定义开始之处到文件结尾)。
局部静态变量
方法:在局部变量前加上关键字static,全局变量就定义成了一个全局静态变量。
位置:位于静态储存区,整个程序运行期间都一直存在。
初始化:未经初始化的全局今天变量会被自动初始化为0(自动对象的值是任意的,除非被显式初始化)。
作用域:局部。当定义它的函数或者语句块结束的时候,作用域结束。但局部静态变量离开作用域后并没有销毁,而仍然驻留在内存中,直到再次调用函数,才能访问,并且其值不会改变,
静态函数
在函数返回类型前加上关键字static,函数就定义成了一个静态函数。函数的定义和声明默认都是extern的,但是静态函数只是它声明的函数的文件中可见,不能被其他文件所用,不会同其他cpp中的同名函数引起冲突。即不要在头文件中声明静态全局函数,不要在cpp中声明非静态的全局函数。如果你要在多个cpp中复用该函数就应该把它加入到头文件中,否则应该在cpp内部加上static修饰。
类的静态成员
在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有成员中共享的成员,而不是某个对象的成员。对于多个对象来说,静态数据成员只存储在一处,供所有对象共用。
类的静态函数
静态成员函数和静态数据成员一样,他们都属于类的静态成员,他们都不是对象成员。因此,对静态成员的引用不需要对象名。在静态成员函数的实现中不能只能引用类中说明的非静态成员,只能通过引用对象来引用。
C++中的四种cast转换
C++中的四种类型转换分别是:static_cast、dynamic_cast、const_cast、reinterpret_cast。
const_cast
static_cast
用于各种隐式转换。比如非const转const,void*转指针等,static_cast能用于多态向上转化,向下转化也能成功, 但是结果未知。
dynamic_cast
动态类型转换,只能用于含有虚函数的类,用于类层次件的向上或者向下转化。只能换指针或引用。向下转化时, 如果时非法的,对于指针返回NULL,对于引用抛异常。
reinterpret_cast
几乎什么都能转,但是可能会出问题,尽量少用
C的强制转换表面看起来功能强大什么都能转,但是转化不够明确,不能进行错误检查,容易出错。
C/C++中引用与指针的区别
引用和指针的主要区别如下:
- 指针有自己的一块空间地址,而引用只是一个别名。
- 使用sizeof看一个指针的大小是4,而引用则是被引用对象的大小。
- 指针可以被初始化为NULL,而引用必须被初始化且必须是一个已有对象的引用。
- 作为参数传递时,指针需要被破解引用才可以对对象进行操作,而直接对引用的修改都会改变引用所指向的对象。
- 可以有const指针,但是没有const引用。
- 指针在使用过程中可以指向其他对象,但是引用只能是一个对象的引用,不能被改变。
- 指针可以是多级指针(*p),而引用只能为一级。
- 指针和引用使用++ —等运算符的意义不一样。
返回动态分配的对象或内存,必须使用指针,引用可能对引起内存泄漏。
C/C++中数组与指针的区别
指针和数组的主要区别如下:
指针是保存数据的地址,而数组保存的是数据。
- 指针只能间接访问数据,首先获得指针内容,然后将其作为地址,从地址中提取数据,而数组能直接访问数据。
- 指针通常用于动态的数据结构,而数组通常用于固定数目且数据类型相同的元素。
- 指针通过malooc分配内存,free释放,数组是隐式的分配和删除。
- 指针通常指向匿名数据,操作匿名函数,数组自身即为数据名。
C/C++中的野指针
野指针就是指向一个已删除的对象或者未申请访问受限的内存区域的指针。C/C++中的函数指针:
定义
函数指针是指向函数的变量。
函数指针首先是一个指针变量,该指针变量指向一个具体的函数。者正如有指针变量乐意指向整型变量、字符型、数组一样,只不过这里是指向函数。
C在编译时的时候,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的变量指针变量后,可用该指针调用函数,就如同制指针变量可引用其他类型变量一样。用途
调用函数和做函数饿参数,例回调掉函数。示例
char *fun(char *p){...} //函数fun
char *(pf)(char *p); //函数指针pf
pf = fun; //函数指针pf指向函数fun
pf(p); //通过函数指针pf调用函数fun
C++中的四种智能指针
c++里面的四个智能指针:auto_ptr,shared_ptr,weak_ptr,unique_ptr其中后三个c++11已经支持,auto_ptr已被弃用。
智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上避免这个问题,因为智能指针就是一个类,超出了类的作用域时类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是函数结束时自动释放资源,不需要手动释放内存空间。auto_ptr
采用所有权模式:
此时不会报错,p2剥夺类p1的所有权,但是当程序运行时访问p1时将会报错。所有auto_ptr的缺点是:潜在的内存崩溃问题。auto_ptr<string> p1(new string("Hello world!"));
auto_ptr<string> p2;
p2 = p1; //auto_ptr不会报错。
unique_ptr(替换auto_ptr)
unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄漏特别有用,其也采用所有权模式:unique_ptr<string> p3(new string("Hello world!"));
unique_ptr<string> p4;
p2 = p1; //unique_ptr会报错!
shared_ptr
shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时释放。从名字share就可以看出了资源可以被多个指针共享,它会使用计数机机制来表明资源会被几个指针共享。他可以通过成员还输use_count()来查看资源的所有者个数。出了可以通过new来构造,还可以通过传入auto_ptr、unique_ptr、weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权上的局限性(auto_ptr是独占的),在使用引用计数的机制提供了可以共享所有权的智能指针。
成员函数:
use_count 返回引用计数的个数
unique_count 返回是否独占所有权(use_count为1)
swap 交换两个shared_ptr对象(即交换所拥有的对象)
reset 放弃内部对象的所有权或者拥有对象的变更,会引起原油对象的引用计数的减少
get 返回返回内部对象(指针),与直接使用对象等效(重载了())。weak_ptr
weak_ptr是一种不控制对象声明周期的智能指针,它指向一个shared_ptr管理的对象,进行该对象的内存管理的是那个强引用的shared_ptr,weak_ptr只是提供了对管理对象的访问手段。weak_ptr设计的目的是为配合shared_ptr而引入的一种智能指针来协助。
其可以用来解决智能指针内存泄漏的问题:
当两个对象互相使用一个shared_ptr成员变指向对象,会造成循环引用,使引用计数器失效,从而导致内存泄漏。weak_ptr的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但不指向引用计数的共享内存,但是其可以检测到所管理的对象是否被释放,从而避免非法访问。STL中的fork函数
Fork:创建一个和当前进程映像一样的进程可以通过fork()系统调用。
成功调用fork()会创建一个新的进程,它几乎与调用fork的进程一模一样,这里连个进程都会继续运行。在子进程中,成功的fork()调用会返回0。在父进程的fork()返回子进程的pid。如果出现错误,fork()返回一个负值。#inlude<sys/types.h>
#include<unistd.h>
pid_t fork(void);
最常见的fork()用法是创建一个新的进程,然后使用exec()载入二进制映像,替换当前进程的映像。在这种情况下,派生(fork)了新的进程,而这个子进程会指向一个新的二进制可执行文件的映像。
在早期的Unix系统中,创建进程比较原始。当调用fork时,内核会把所有的内部数据结构复制一份,复制进程的页表项,然后把父进程的地址空间中的内容逐页的复制到子进程的地址空间中。但从内核角度来说,逐页的复制方式是十分耗时的。现代的Unix系统采取了更多的优化,例如Linux,采用了写时复制的方法,而不是对父进程空间进程整体复制。C++中的析构函数的作用
析构函数与构造函数对应,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。
析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,例如~stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括void类型)。只能有一个析构函数,不能重载。
如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。
如果一个类中有指针,且在使用的过程中动态的申请了内存,那么最好显示构造析构函数在销毁类之前,释放掉申请的内存空间,避免内存泄漏。
类析构顺序: 1)派生类本身的析构函数; 2)对象成员析构函数; 3)基类析构函数。C++中的静态函数与虚函数
静态函数在编译的时候就已经确定运行时机,虚函数在运行的时候动态绑定。虚函数因为用了虚函数表机制,调用的时候会增加一.次内存开销。C++中的多态与虚函数
多态的实现主要分为静态多态和动态多态,静态多态主要是重载,在编译的时候就已经确定;动态多态是用虚函数机制实现的,在运行期间动态绑定。举个例子:-一个父类类型的指针指向一个子类对象时候,使用父类的指针去调用子类中重写了的父类中的虚函数的时候,会调用子类重写过后的函数,在父类中声明为加了virtual关键字的函数,在子类中重写时候不需要加virtual也是虚函数。
虚函数的实现:在有虚函数的类中,类的最开始部分是一一个虚函数表的指针,这个指针指向一个虚函数表,表中放了虚函数的地址,实际的虚函数在代码段(. text)中。当子类继承了父类的时候也会继承其虚函数表,当子类重写父类中虚函数时候,会将其继承到的虚函数表中的地址替换为重新写的函数地址。使用了虚函数,会增加访问内存开销,降低效率。C++中的重载与覆盖
重载
两个函数名相同,但是参数列表不同(个数,类型),返回值类型没有要求,在同一作用域中。重写
子类继承了父类,父类中的函数是虚函数,在子类中重新定义了这个虚函数,这种情况是重写。在main函数执行之前执行的函数
__attribute ((constructor)) void before (){
printf(" before mainln" );
}
extren “C” 关键字
C++调用C函数需要extern C,因为C语言没有函数重载。new/delete与malloc/free的区别
new/delete是C++的关键字,而malloc/free是C语言的库函数,后者使用必须指明申请内存空间的大小,对于类类型的对象,后者不会调用构造函数和析构函数。深拷贝与浅拷贝
当出现类的等号赋值时,即会调用拷贝函数
1. 在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的;但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中 的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象,所以,此时,必须采用深拷贝。
2. 深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必须要用深拷贝。