C/C++容易混淆的小知识点

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/snow_5288/article/details/76984085

1、函数传指针和传引用的区别?
1>指针定义时可以不初始化,但引用不行;
2>引用只能和一个实体结合,而指针可和多个实体结合;
3>自加减意义不同。指针的++表示指针向后偏移类型个字节,而引用则是+1,–类似;
4>sizeof求值的意义不同。Sizeof(指针)是指针所占的字节数,32位平台下为4,64位平台下为8(数组名除外),sizeof(引用)是指引用所指实体类型的大小;
5>没有空引用,但是有空指针;
6>有多级指针,但没有多级引用;
7>引用比指针用起来更安全,不用判空。
注意:指针也可以进行引用

2、C++中的封装:
隐藏对象的属性和实现细节,仅仅对外提供公开接口和对象进行交互,将数据和操作数据的方法进行有机结合。

3、必须在构造函数的成员初始化列表中进行初始化的数据成员:
引用数据成员、const数据成员、类类型成员(该类没有缺省的构造函数)。

4、面向对象与面向过程的区别?
面向过程程序设计方法采用函数(或过程)来描述对数据的操作,但又将函数与其操作的数据分离开来。
面向对象程序设计方法是将数据和对象的操作封装在一起,作为一个整体来处理。
面向过程程序设计以过程为中心,难于维护。
面向对象程序设计以数据为中心,数据相对功能而言,有较强的稳定性,因此更易于维护。

5、static关键字
static可修饰局部变量、全局变量和函数。
在修饰全局的变量和函数时:在文件作用域中使用可改变链接属性,仅在当前文件中可以使用。
修饰局部变量时:
1.在静态区开辟;
2.生命周期为整个函数的运行时间;
3.默认初始化为0;
4.变量具有记忆作用,即每次保存上一次调用后的值;
5.在函数第一次调用时创建,构造函数只调用一次。
修饰类的成员变量和函数:
静态成员为所有对象所共享,不属于某个实例
类静态成员即可用类名::静态成员或者对象(.静态成员函数)来访问
类静态成员变量必须在类内声明类外定义,定义时不添加static关键字
类的静态成员函数没有默认的this指针,因此在它里面不能使用任何非静态成员
静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值,const修饰符等参数。

6、malloc、free和new、delete的区别:
malloc、free为函数,new、delete为操作符;
malloc的参数为要申请的字节数,new直接跟类型;
malloc的返回值为void*,并且返回时需要判空,new不需要判空,申请空间失败时会直接抛异常;
new申请单个类型的空间时可对其进行初始化,同时还可以数组空间;
malloc、free调用时不会调用构造函数和析构函数,但new、delete会;
new的底层会调用malloc,delete的底层会调用free。
1.operator new/operator delete、 operator new[]/operator delete[] 和 malloc/free用法一样。
2. 他们只负责分配空间/释放空间,不会调用对象构造函数/析构函数来初始化/清理对象。
3. 实际operator new和operator delete只是malloc和free的一层封装

7、定位new表达式
是在已分配的原始内存空间中调用构造函数初始化一个对象。
new (place_address) type
new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表。

8、sizeof 和 strlen的区别?
Sizeof用来求取类型或变量所占的字节数,strlen则用来求取字符串的长度,不包括’\0’;
Sizeof 求字符个数时会包含‘\0’,strlen 不会;
Sizeof(数组名)数组名代表整个数组的大小,strlen(数组名)代表数组首元素的地址;
Sizeof(类型),sizeof(变量),sizeof 变量,strlen()必须加括号;
Sizeof在编译时求字节数,strlen在运行时才计算;
Sizeof+(类型或变量),strlen(字符串地址)。

9、debug和release的区别?
Debug通常称为调试版本,它包含调试信息,并且不做任何优化,便于程序员进行调试。
Release称为发布版本,它往往进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好的使用。

10、局部性原理
时间局部性原理:一个值正在被访问,那么近期它有可能再次被访问;
空间局部性原理:一个值正在被访问,那么地址与它临近的值有可能近期再次被访问。

11、C语言和C++的对比
1>文件后缀不同。C语言通常以.c结尾,而C++通常以.cpp结尾
2>默认返回值不同。如果一个函数没有指定返回值,则C语言默认返回int类型,C++默认返回void类型
3>默认参数列表不同。在没有指定参数列表时,C语言默认可接收任意多个参数,C++默认为void,不接收任何类型的参数
4>缺省参数。C语言的缺省参数为int类型,C++为void类型。
最重要的是解决问题的思想和方法不一样,C语言是面向过程的,C++是面向对象的。

12、宏和函数的比较
这里写图片描述

11、构造函数的特性
1>构造函数没有返回值
2>构造函数有初始化列表但可以不用
3>构造函数的名字必须与类名相同
4>在对象被创建时,构造函数被编译器自动调用,且在对象的生命周期内智能调一次
5>构造函数可以重载,实参决定了调用哪个构造函数
7>无参构造函数和带有缺省值的构造函数都认为是缺省构造函数,且智能有一个

12、赋值兼容规则
子类对象可以直接赋值给父类对象;
父类对象不能直接赋值给子类对象;
父类的指针或引用可以直接指向子类的对象;
子类的指针或引用不可以直接指向给父类的对象(强转可以)。

13、多态
概念:所谓多态性就是指达能够不同的对象受到不同的消息时产生不同的动作
引入:基类的指针或引用指向子类的对象时,它只能访问派生类中从基类继承来的成员,而不能访问派生类的新增成员,引用虚函数可解决此问题
动态绑定条件:基类中的成员函数给成虚函数;调用虚函数时一定要通过基类的指针或引用调用;虚函数必须在派生类中重写(同时满足)。

14、函数重载、同名隐藏、重写(覆盖/覆写)
函数重载:函数名相同、同一作用域、参数列表不同(类型、个数、顺序)
同名隐藏:一个处于基类,一个处于派生类、函数名相同
重写(覆盖/覆写):一个处于基类一个处于派生类、都是虚函数、函数名相同、参数列表相同、函数的返回值相同(协变除外)
协变:返回值一个是基类的指针或引用,一个是派生类的指针或引用

15、什么时候析构函数需要设置为虚函数
在多态的场景下,当一个基类的指针或引用指向一个派生类对象时,由于是基类的指针所以会调用基类的析构函数,此时属于派生类的那部分空间并没有释放,因此造成内存泄漏,所以这种场景下必须基类的析构函数必须设置为虚函数,此时调用析构函数的时候就会调用派生类的析构函数。

16、编译器什么时候会为我们合成默认的构造函数
1>有一个A类定义有自己的缺省构造函数,B类没有显示定义自己的构造函数但类中包含A类的对象,此时编译器会为B类合成默认的构造函数来调用A类已有的构造函数。
2>如果B类和D类处于继承关系(class D:public B)B类包含自己的缺省构造函数,D类无构造函数,此时编译器会为D类合成默认的构造函数来调用B类已有的缺省构造函数。
3>虚拟继承中,如果派生类没有显示的定义自己的构造函数,则编译器将会给它合成默认的构造函数,并且在构造对象的时候在对象的前4个字节放入指向虚基表的指针。
4>多态中,如果基类包含虚函数,如果派生类没有显示定义构造函数,则编译器会为其合成默认的构造函数,并且将指向虚表(虚函数表)的指针存放在对象的前4个字节。

17、虚表的创建
基类:按照虚函数的声明顺序一次将虚函数的地址填入虚表中,最后位置放0
派生类:先拷贝一份基类的虚表
如果派生类对基类的某些函数进行重写,则派生类会将虚表相同便宜位置的函数修改为派生类自己的虚函数地址;
如果派生类新增了不同于基类的虚函数,则将其紧放在虚表末尾,最后位置放0

18、STL的6大组件
容器、迭代器、算法、适配器、空间配置器、仿函数(函数对象)

19、程序的执行过程
预处理—-编译—-汇编—-链接—-执行
预处理:删除注释、宏替换、处理条件编译、包含头文件、添加行号、文件名等
编译 :词法分析、语法分析、语义分析、优化之后产生相应的汇编代码
汇编:将汇编代码编程计算机可以执行的二进制指令
链接:导出符号表、地址重定向表、位解决的符号表
执行:执行代码的具体逻辑

20、异常处理
C语言:
1>终止程序
2>返回一个表示错误的值,附加错误码
3>返回一个合法值,让程序处于某种非法的状态
4>调用一个预先准备好出现错误的情况下调用的函数
5>暴力解决:abort exit
6>使用goto语句(只能在函数内部跳转)
7>setjmp和longjmp组合使用
C++:
throw(抛出异常) try(检查是否发生异常) catch(处理捕获的异常)

21、类型转换
C语言:隐式类型转换(如整形提升)和显示类型转换(强转)
C++:static_cast:用于非静态类型,类似于隐式类型转换
const_cast:去除变量的常属性,方便赋值
reinterpret_cast:适用于两个不同类型之间的转换(不安全)
dynamic_cast:用于将父类对象/指针转换为子类对象/指针,类中必须包含虚函数
Expilicit:防止单参数的构造函数进行饮食类型的转换
Volatile:防止编译器进行优化,保证内存的可见性

22、调用约定
这里写图片描述

23、const关键字
(1)可以定义const常量,具有不可变性。
例如:const int Max=100; Max++会产生错误;
(2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。
例如: void f(const int i) { ………} 编译器就会知道i是一个常量,不允许修改;
(3)可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。 同宏定义一样,可以做到不变则已,一变都变!
如(1)中,如果想修改Max的内容,只需要:const int Max=you want;即可!
(4)可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。 还是上面的例子,如果在函数体内修改了i,编译器就会报错;
例如: void f(const int i) { i=10;//error! }
(5) 可以节省空间,避免不必要的内存分配。 例如:

#define PI 3.14159 //常量宏
const double Pi=3.14159; //此时并未将Pi放入RAM中 ......
double i=Pi; //此时为Pi分配内存,以后不再分配!
double I=PI; //编译期间进行宏替换,分配内存
double j=Pi; //没有内存分配
double J=PI; //再进行宏替换,又一次分配内存!

const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干份拷贝。
(6)提高了效率。
编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
C语言和C++ 中const的区别:
C:const定义变量时变量还是变量,只是它具有了常属性,不可修改。
C++:const定义的变量,编译器认为它时常量。
验证:arr[n]在C++中可以,在C语言中不行。

24、struct和class
struct成员的默认访问权限是public,class成员的默认访问权限是private。
默认的继承访问权限:struct是public的,class是private的。

25、模板为什么不支持分离编译
C++编译器的工作简介:
在 C++ 标准中提到,一个编译单元 [ Translation Unit ] 是指一个.cpp文件以及它所include的所有.h文件。.h文件里的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp 文件为一个.obj文件,后者拥有PE [ Portable Executable,即 Windows 可执行文件 ] 文件格式,并且本身包含的就已经是二进制码。但是,不一定能够执行,因为并不保证其中一定有main 函数。当编译器将一个工程里的所有.cpp文件以分离的方式编译完毕后,再由连接器 [ linker ] 进行连接成为一个.exe文件。
下面给出一个例子说明原因: [将模板和它的实现分离]

//-------------test.h----------------//
template<class T>
class A
{
public:
void f(); //这里只是个声明
};
//---------------test.cpp-------------//
#include”test.h”
template<class T>
void A<T>::f() //模板的实现
{
…//do something
}

//---------------main.cpp---------------//
#include”test.h”
int main()
{
A<int> a;//模板的调用过程
a. f();//f具有外部属性
}

在main.cpp中由于编译时test.h里的代码被展开,所以找到了f的声明,并同时调用它找它的实现代码,本来f函数的实现代码在test.cpp的文件里实现,但由于在test.cpp里没有对f函数进行实例化,所以模板并不会为其生成相关代码,这就导致了main函数链接时并没有找到f函数的实现代码,因而报出链接错误(无法解析的外部符号)。

26、C语言为什么不支持重载
因为c++有名命修饰,他会把每一个参数的类型的用一个字符串来表示加到函数名上。所以重载的两个同名函数编译出来的函数名不同,所以不冲突支持重载。
c没有命名修饰,函数叫啥编译出来只是给函数名前加了下划线而已,同名函数当然会冲突的,因此不能重载。

若有问题,欢迎指出…

展开阅读全文

没有更多推荐了,返回首页