zoukankan      html  css  js  c++  java
  • c和c++的区别

    1、C和C++的区别

    1)C是面向过程的语言,是一个结构化的语言,考虑如何通过一个过程对输入进行处理得到输出;C++是面向对象的语言,主要特征是“封装、继承和多态”。封装隐藏了实现细节,使得代码模块化;派生类可以继承父类的数据和方法,扩展了已经存在的模块,实现了代码重用;多态则是“一个接口,多种实现”,通过派生类重写父类的虚函数,实现了接口的重用。

    2)C和C++动态管理内存的方法不一样,C是使用malloc/free,而C++除此之外还有new/delete关键字。

    3)C++支持函数重载,C不支持函数重载

    4)C++中有引用,C中不存在引用的概念

    2、C++中指针和引用的区别

    1)指针是一个新的变量,存储了另一个变量的地址,我们可以通过访问这个地址来修改另一个变量;

    引用只是一个别名,还是变量本身,对引用的任何操作就是对变量本身进行操作,以达到修改变量的目的

    2)引用只有一级,而指针可以有多级

    3)指针传参的时候,还是值传递,指针本身的值不可以修改,需要通过解引用才能对指向的对象进行操作

    引用传参的时候,传进来的就是变量本身,因此变量可以被修改

    3、结构体struct和共同体union(联合)的区别

    结构体:将不同类型的数据组合成一个整体,是自定义类型

    共同体:不同类型的几个变量共同占用一段内存

    1)结构体中的每个成员都有自己独立的地址,它们是同时存在的;

    共同体中的所有成员占用同一段内存,它们不能同时存在;

    2)sizeof(struct)是内存对齐后所有成员长度的总和,sizeof(union)是内存对齐后最长数据成员的长度、

    4、#define和const的区别

    1)#define定义的常量没有类型,所给出的是一个立即数;const定义的常量有类型名字,存放在静态区域

    2)处理阶段不同,#define定义的宏变量在预处理时进行替换,可能有多个拷贝,const所定义的变量在编译时确定其值,只有一个拷贝。

    3)#define定义的常量是不可以用指针去指向,const定义的常量可以用指针去指向该常量的地址

    4)#define可以定义简单的函数,const不可以定义函数

    5、重载overload,覆盖override,重写overwrite,这三者之间的区别

    1)overload,将语义相近的几个函数用同一个名字表示,但是参数和返回值不同,这就是函数重载

    特征:相同范围(同一个类中)、函数名字相同、参数不同、virtual关键字可有可无

    2)override,派生类覆盖基类的虚函数,实现接口的重用

    特征:不同范围(基类和派生类)、函数名字相同、参数相同、基类中必须有virtual关键字(必须是虚函数)

    3)overwrite,派生类屏蔽了其同名的基类函数

    特征:不同范围(基类和派生类)、函数名字相同、参数不同或者参数相同且无virtual关键字

    7、delete和delete[]的区别

    delete只会调用一次析构函数,而delete[]会调用每个成员的析构函数

    用new分配的内存用delete释放,用new[]分配的内存用delete[]释放

    8、STL库用过吗?常见的STL容器有哪些?算法用过几个?

    STL包括两部分内容:容器和算法

    容器即存放数据的地方,比如array, vector,分为两类,序列式容器和关联式容器

    序列式容器,其中的元素不一定有序,但是都可以被排序,比如vector,list,queue,stack,heap, priority-queue, slist

    关联式容器,内部结构是一个平衡二叉树,每个元素都有一个键值和一个实值,比如map, set, hashtable, hash_set

    算法有排序,复制等,以及各个容器特定的算法

    迭代器是STL的精髓,迭代器提供了一种方法,使得它能够按照顺序访问某个容器所含的各个元素,但无需暴露该容器的内部结构,它将容器和算法分开,让二者独立设计。

    9、const知道吗?解释一下其作用

    const修饰类的成员变量,表示常量不可能被修改

    const修饰类的成员函数,表示该函数不会修改类中的数据成员,不会调用其他非const的成员函数

    10、虚函数是怎么实现的

    每一个含有虚函数的类都至少有有一个与之对应的虚函数表,其中存放着该类所有虚函数对应的函数指针(地址),

    类的示例对象不包含虚函数表,只有虚指针;

    派生类会生成一个兼容基类的虚函数表。

    11、堆和栈的区别

    1)栈 stack 存放函数的参数值、局部变量,由编译器自动分配释放

    堆heap,是由new分配的内存块,由应用程序控制,需要程序员手动利用delete释放,如果没有,程序结束后,操作系统自动回收

    2)因为堆的分配需要使用频繁的new/delete,造成内存空间的不连续,会有大量的碎片

    3)堆的生长空间向上,地址越大,栈的生长空间向下,地址越小

    12、关键字static的作用

    1)函数体内: static 修饰的局部变量作用范围为该函数体,不同于auto变量,其内存只被分配一次,因此其值在下次调用的时候维持了上次的值

    2)模块内:static修饰全局变量或全局函数,可以被模块内的所有函数访问,但是不能被模块外的其他函数访问,使用范围限制在声明它的模块内

    3)类中:修饰成员变量,表示该变量属于整个类所有,对类的所有对象只有一份拷贝

    4)类中:修饰成员函数,表示该函数属于整个类所有,不接受this指针,只能访问类中的static成员变量

    红黑树是一种特殊的二叉查找树

    1)每个节点或者是黑色,或者是红色 

    2)根节点是黑色

    3) 每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]

    4)如果一个节点是红色的,则它的子节点必须是黑色的

    5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

    特性4)5)决定了没有一条路径会比其他路径长出2倍,因此红黑树是接近平衡的二叉树。

    15、什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法?

    动态分配内存所开辟的空间,在使用完毕后未手动释放,导致一直占据该内存,即为内存泄漏。

    方法:malloc/free要配套,对指针赋值的时候应该注意被赋值的指针是否需要释放;使用的时候记得指针的长度,防止越界

    16、定义和声明的区别

    声明是告诉编译器变量的类型和名字,不会为变量分配空间

    定义需要分配空间,同一个变量可以被声明多次,但是只能被定义一次

    17、C++文件编译与执行的四个阶段

    1)预处理:根据文件中的预处理指令来修改源文件的内容

    2)编译:编译成汇编代码

    3)汇编:把汇编代码翻译成目标机器指令

    4)链接:链接目标代码生成可执行程序

    18、STL中的vector的实现,是怎么扩容的?

    vector使用的注意点及其原因,频繁对vector调用push_back()对性能的影响和原因。 
    vector就是一个动态增长的数组,里面有一个指针指向一片连续的空间,当空间装不下的时候,会申请一片更大的空间,将原来的数据拷贝过去,并释放原来的旧空间。当删除的时候空间并不会被释放,只是清空了里面的数据。对比array是静态空间一旦配置了就不能改变大小。

    vector的动态增加大小的时候,并不是在原有的空间上持续新的空间(无法保证原空间的后面还有可供配置的空间),而是以原大小的两倍另外配置一块较大的空间,然后将原内容拷贝过来,并释放原空间。在VS下是1.5倍扩容,在GCC下是2倍扩容。

    在原来空间不够存储新值时,每次调用push_back方法都会重新分配新的空间以满足新数据的添加操作。如果在程序中频繁进行这种操作,还是比较消耗性能的。

    19、STL中unordered_map和map的区别

    map是STL中的一个关联容器,提供键值对的数据管理。底层通过红黑树来实现,实际上是二叉排序树和非严格意义上的二叉平衡树。所以在map内部所有的数据都是有序的,且map的查询、插入、删除操作的时间复杂度都是O(logN)。

    unordered_map和map类似,都是存储key-value对,可以通过key快速索引到value,不同的是unordered_map不会根据key进行排序。unordered_map底层是一个防冗余的哈希表,存储时根据key的hash值判断元素是否相同,即unoredered_map内部是无序的

    20、C++的内存管理

    在C++中,内存被分成五个区:栈、堆、自由存储区、静态存储区、常量区

    栈:存放函数的参数和局部变量,编译器自动分配和释放

    堆:new关键字动态分配的内存,由程序员手动进行释放,否则程序结束后,由操作系统自动进行回收

    自由存储区:由malloc分配的内存,和堆十分相似,由对应的free进行释放

    全局/静态存储区:存放全局变量和静态变量

    常量区:存放常量,不允许被修改

    21、 构造函数为什么一般不定义为虚函数?而析构函数一般写成虚函数的原因 ?

    1、构造函数不能声明为虚函数

    1)因为创建一个对象时需要确定对象的类型,而虚函数是在运行时确定其类型的。而在构造一个对象时,由于对象还未创建成功,编译器无法知道对象的实际类型,是类本身还是类的派生类等等

    2)虚函数的调用需要虚函数表指针,而该指针存放在对象的内存空间中;若构造函数声明为虚函数,那么由于对象还未创建,还没有内存空间,更没有虚函数表地址用来调用虚函数即构造函数了

    2、析构函数最好声明为虚函数

    首先析构函数可以为虚函数,当析构一个指向派生类的基类指针时,最好将基类的析构函数声明为虚函数,否则可以存在内存泄露的问题。

    如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除指向派生类的基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。

    22、静态绑定和动态绑定的介绍

    静态绑定和动态绑定是C++多态性的一种特性

    1)对象的静态类型和动态类型

    静态类型:对象在声明时采用的类型,在编译时确定

    动态类型:当前对象所指的类型,在运行期决定,对象的动态类型可变,静态类型无法更改

    2)静态绑定和动态绑定

    静态绑定:绑定的是对象的静态类型,函数依赖于对象的静态类型,在编译期确定

    动态绑定:绑定的是对象的动态类型,函数依赖于对象的动态类型,在运行期确定

    只有虚函数才使用的是动态绑定,其他的全部是静态绑定

    23、 引用是否能实现动态绑定,为什么引用可以实现

    可以。因为引用(或指针)既可以指向基类对象也可以指向派生类对象,这一事实是动态绑定的关键。用引用(或指针)调用的虚函数在运行时确定,被调用的函数是引用(或指针)所指的对象的实际类型所定义的。

    24、深拷贝和浅拷贝的区别

    深拷贝和浅拷贝可以简单的理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,如果资源重新分配了就是深拷贝;反之没有重新分配资源,就是浅拷贝。

    25、 什么情况下会调用拷贝构造函数(三种情况) 

    系统自动生成的构造函数:普通构造函数和拷贝构造函数 (在没有定义对应的构造函数的时候)

    生成一个实例化的对象会调用一次普通构造函数,而用一个对象去实例化一个新的对象所调用的就是拷贝构造函数

    调用拷贝构造函数的情形:

    1)用类的一个对象去初始化另一个对象的时候

    2)当函数的参数是类的对象时,就是值传递的时候,如果是引用传递则不会调用

    3)当函数的返回值是类的对象或者引用的时候

    26、纯虚函数

    纯虚函数是只有声明没有实现的虚函数,是对子类的约束,是接口继承

    包含纯虚函数的类是抽象类,它不能被实例化,只有实现了这个纯虚函数的子类才能生成对象

    普通函数是静态编译的,没有运行时多态

    27、什么是野指针

    野指针不是NULL指针,是未初始化或者未清零的指针,它指向的内存地址不是程序员所期望的,可能指向了受限的内存

    成因:

    1)指针变量没有被初始化

    2)指针指向的内存被释放了,但是指针没有置NULL 

    3)指针超过了变量了的作用范围,比如b[10],指针b+11

    28、线程安全和线程不安全

    线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可以使用,不会出现数据不一致或者数据污染。

    线程不安全就是不提供数据访问保护,有可能多个线程先后更改数据所得到的数据就是脏数据。

    29、C++中内存泄漏的几种情况

    内存泄漏是指己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

    1)类的构造函数和析构函数中new和delete没有配套

    2)在释放对象数组时没有使用delete[],使用了delete

    3)没有将基类的析构函数定义为虚函数,当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确释放,因此造成内存泄露

    4)没有正确的清楚嵌套的对象指针

    30、栈溢出的原因以及解决方法

    1)函数调用层次过深,每调用一次,函数的参数、局部变量等信息就压一次栈

    2)局部变量体积太大。

    解决办法大致说来也有两种:

    1> 增加栈内存的数目;增加栈内存方法如下,在vc6种依次选择Project->Setting->Link,在Category中选择output,在Reserve中输入16进制的栈内存大小如:0x10000000

    2> 使用堆内存;具体实现由很多种方法可以直接把数组定义改成指针,然后动态申请内存;也可以把局部变量变成全局变量,一个偷懒的办法是直接在定义前边加个static,呵呵,直接变成静态变量(实质就是全局变量)

    31、C++标准库vector以及迭代器

    每种容器类型都定义了自己的迭代器类型,每种容器都定义了一队命名为begin和end的函数,用于返回迭代器。

    迭代器是容器的精髓,它提供了一种方法使得它能够按照顺序访问某个容器所含的各个元素,但无需暴露该容器的内部结构,它将容器和算法分开,让二者独立设计。

  • 相关阅读:
    逆向分析实战
    打印工程内所有方法的调用
    JAVA B/S系统实现客户端屏幕截图,Java版的QQ截图
    让input支持 ctrl v上传粘贴图片? 让input支持QQ截图或剪切板中的图像数据(Java实现保存)
    spring4 security 4 +websocket 实现单点登录
    Spring quartz 单机、集群+websocket集群实现文本、图片、声音、文件下载及推送、接收及显示
    spring 4.2.0后jdbcTemplate中不用queryForLong了(之系统升级发现)
    友好解决POI导入Excel文件行是不是为空
    解决POI读取Excel如何判断行是不是为空
    集群: 如何在spring 任务中 获得集群中的一个web 容器的端口号?
  • 原文地址:https://www.cnblogs.com/limingqi/p/12408049.html
Copyright © 2011-2022 走看看