文章目录
malloc/calloc/realloc的区别?
-
函数名字不同,参数类型不同;
-
calloc函数会对申请空间初始化,并且初始化为0;malloc和realloc不会进行初始化
-
malloc函数申请空间必须使用memset进行初始化;
-
realloc函数是对已经存在的空间进行调整,当第一个参数传入NULL的时候和malloc函数一样,其调整分为两种请况:
-
-
调整的空间比原有空间大
a.大了一点
调整方法:
(1)直接延伸申请空间;
(2)返回空间首地址。b.大了很多
调整方法:
(1)重新开辟新空间;
(2)将旧空间的内容拷贝到新空间中;
(3)释放旧空间;
(4)返回新空间的首地址。 -
调整的空间比原有空间小
调整方法:
(1)将原有空间缩小;
(2)返回旧空间首地址。
-
operator new与operator delete函数
-
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
-
operator new 实际也是通过malloc来申请空间,如果malloc申请空间 成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
形参与实参的区别?
- 形参变量只有在被调用时才分配内存单元,在调用结束时, 即刻释放所分配的内存单元。因此,形参只有在函数内部有效。 函数调用结束返回主调函数后则不能再使用该形参变量。
- 实参可以是常量、变量、表达式、函数等, 无论实参是何种类型,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值,会产生一个临时变量。
- 实参和形参在数量上,类型上,顺序上应严格一致, 否则会发生“类型不匹配”的错误。
- 函数调用中发生的数据传送是单向的。 即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。
- 当形参和实参不是指针类型时,在该函数运行时,形参和实参是不同的变量,他们在内存中位于不同的位置,形参将实参的内容复制一份,在该函数运行结束的时候形参被释放,而实参内容不会改变。
值传递、指针传递、引用传递的区别和效率
-
值传递
在函数调用时,将实参的值复制给形参。函数内部对形参的修改不会影响到实参本身。缺点是在函数调用时需要进行值的复制,对于较大的数据对象,值传递会导致额外的开销。
-
指针传递
指在函数调用时,将实参的地址传递给函数形参,形参是一个指向实参的指针。同样有一个形参向函数所属的栈拷贝数据的过程,但拷贝的数据是一个固定为4字节的地址。函数内部可以通过指针修改实参的值,因为指针指向实参的地址。
-
引用传递
将实参的引用传递给函数形参,形参是实参的别名。同样有上述的数据拷贝过程,但其是针对地址的,相当于为该数据所在的地址起了一个别名。函数内部对形参的修改会直接影响到实参本身。
-
效率方面
指针传递和引用传递比值传递效率高,一般主张使用引用传递,它避免了复制数据的开销,并且不需要额外的指针操作,通常是推荐的传参方式。
-
对于较小的数据对象,值传递可能是合适的选择。对于较大的数据对象,引用传递可能是更高效的方式。指针传递则通常用于需要在函数内部修改传入对象的值的情况。
派生类构造函数的执行顺序
- 虚基类的构造函数(多个虚基类则按照继承的顺序执行构造函数)
- 基类的构造函数(多个普通基类则按照继承的顺序执行构造函数)
- 类类型的成员对象的构造函数(按照成员对象在类中定义的顺序执行)
- 派生类自己的构造函数
有哪些情况必须用到成员列表初始化?为什么用它效率会高一点
-
初始化 const 成员变量,因为 const 成员变量在创建对象后就不能再修改其值,所以必须在构造函数中通过初始化列表来赋予其初始值。
-
初始化引用成员变量,引用在创建对象时必须初始化为引用某个已存在的对象,所以需要在构造函数中通过初始化列表来指定引用的初始值。
-
在派生类构造函数中调用基类构造函数,当类派生自其他类时,派生类的构造函数需要调用基类的构造函数来初始化继承的基类成员。在派生类的构造函数成员初始化列表中使用基类的构造函数进行初始化。
-
调用一个成员类的构造函数
-
注意:成员列表初始化的顺序是由类中的成员声明顺序决定的,不是由初始化列表的顺序决定的
-
效率
对于类中的自定义类型成员,它少了一次调用构造函数的过程,对于内置类型则没有差别
class A{
public:
A(){
cout << "默认构造函数A()" << endl;}
A(int a){
value = a;
cout << "A(int " << value << ")" << endl;
}
int value;
};
class B{
public:
B() : a(1){
b = A(2);
}
A a;
A b;
};
int main(){
B b;
return 0;
执行结果:
A(int 1)
默认构造函数A()
A(int 2)
在构造函数体内部,对象b比对象a多了一次构造函数的调用过程。由于对象成员变量的初始化操作发生在进行构造函数之前,对于内置类型没什么影响,但如果有类成员变量,那么在进入构造函数之前,会先调用一次默认构造函数,进行构造函数后其实是在进行一次赋值操作(对象已存在),所以如果是在构造函数体内进行赋值的话,等于是调用了一次默认构造函数和一次赋值操作,而初始化列表只做一次赋值操作
全局变量和局部变量有什么区别?
-
作用域
全局变量的作用域是整个程序,即从声明的位置开始,直到程序的结束都可以被访问。
局部变量的作用域限定在声明它的代码块(通常是函数体)内部,在代码块外部无法访问。
-
初始化
全局变量在定义时如果没有显式地初始化,会被自动初始化为默认值(例如全局的整型变量会被初始化为0)。
局部变量在定义时不会被自动初始化,其值是未定义的(可能是随机值),需要程序员在使用前进行显式初始化。
-
存储位置
全局变量存储在静态数据区(静态存储区),在程序启动时就会被分配内存,直到程序结束才会释放。
局部变量通常存储在栈上(局部存储区),在其所在的代码块被执行时分配内存,代码块执行完毕后自动释放内存。
怎样判断两个浮点数是否相等?
- 对两个浮点数判断大小和是否相等不能直接用
==
来判断,可能会出错,对于两个浮点数比较只能通过相减并与预先设定的精度比较,记得要取绝对值。浮点数与0的比较也应该注意。与浮点数的表示方式有关。