1. C++中class和struct的主要区别
struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的
另一种说法:
默认继承权限不同:默认情况下,struct的继承以public进行继承,class是以private继承。
默认访问权限不同:class的成员默认权限是private,struct默认是public权限。
2. malloc&free和new&delete的异同点
相同点:
都用于在堆栈上动态申请和释放内存。(备注:new可能不在堆栈上)
区别:
malloc使用时需要指定分配的字节数,不会对对象进行初始化(调用构造函数)。
new使用时能自动根据类型信息计算分配字节大小,会自动调用构造函数。
free不会调用析构函数。
delete会调用析构函数。
malloc vs. new 详细分析
属性
new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要c头文件支持。
参数
使用new关键字时不需要指定内存块的大小,编译器会自动计算。malloc需要显式地指出所需内存的尺寸。
返回类型
new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
分配失败
new内存分配失败时,会抛出bad_alloc异常。malloc分配内存失败时返回NULL。
实现原理
new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。
malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
重载
C++允许重载new/delete操作符,而malloc不允许重载。
内存区域
new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中。(自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配)
3. const关键字
const int a
和int const a
:声明一个整型常量a。
带有指针的情况:
分析方法:直接被const关键字修饰的内容不可变
Example
const int *p1
:分析方法:const直接修饰的是*p1(跳过int关键字),即指针p1指向的数值不可修改,但可以修改指针的指向。(指向常整型的指针。)
int * const p3
:const直接修饰的是p3,即指针的指向不可修改,但是可以修改指针指向的内存的数据(指向整型的常指针)。
const int * const p4
:const修饰了p4和*p4,指针本身和指向的数据都不可修改(指向常整型的常指针)
注解:
在
int *p1
中,p1
存放的是一个地址,*p1
是这个地址指向的内容。因此
const int *p1
中const直接修饰的是*p1,即内容不变,即指向常整型(内容不变)的指针(指向不固定)同理,在
int * const p3
中,const直接修饰的是p3,即地址不变。即指向int型(内容可变)的常指针(指向固定)
const int * const p4
中,const修饰了p4和*p4,地址内容都无法改变。即指向常整型(内容不变)的常指针(指向固定)。
补充:const关键字用于函数中
a. const修饰函数参数
const修饰函数参数,说明函数中不允许更改函数参数的值
void testModifyConst(const int _x) {
_x=5; ///编译出错
}
b. const修饰成员函数
(1)const修饰的成员函数不能修改任何的成员变量(mutable修饰的变量除外)
(2)const成员函数不能调用非const成员函数,因为非const成员函数可以会修改成员变量
写法:
void testConstFunction(int _x) const
const跟在参数括号后面。
Ex:
#include <iostream>
using namespace std;
class Point{
public :
Point(int _x):x(_x){}
void testConstFunction(int _x) const{
///错误,在const成员函数中,不能修改任何类成员变量
x=_x;
///错误,const成员函数不能调用非onst成员函数,因为非const成员函数可以会修改成员变量
modify_x(_x);
}
void modify_x(int _x){
x=_x;
}
int x;
};
c. const修饰返回值
(1)指针传递
如果返回const data,non-const pointer(即指向常数据的可变指针),返回值也必须赋给const data,non-const pointer。因为指针指向的数据是常量不能修改。
const int * mallocA(){ ///const data,non-const pointer
int *a=new int(2);
return a;
}
int main()
{
const int *a = mallocA();
///int *b = mallocA(); ///编译错误
return 0;
}
(2)值传递
如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。所以,对于值传递来说,加const没有太多意义。
4. 重载、覆盖和隐藏
重载:相同范围(同一个类中)、相同名字、参数不同、virtual可有可无
覆盖:范围不同(派生类和基类)、函数名相同、参数相同、基类必须要有virtual。
隐藏:派生类函数屏蔽了同名的基类函数,规则如下:
(1)派生类函数与基类函数同名但是参数不同,此时不论有无virtual关键字,基类函数将被隐藏(和重载不一样,作用范围不同)。
(2)派生类函数与基类函数同名同参数,但是基类函数没有virtual关键字,则基类函数被隐藏(和覆盖不一样,覆盖的情况是基类有virtual关键字)。
5. 指针,指向指针的指针,etc
以下的判断方法:从右向左,由近及远,括号优先
int a
:整型数据a
int *a
即(int *)a
:指向int数据的指针(类型是int*
,变量名a
)
int **a
即(int *)*a
:指向int指针的指针
int a[10]
:10个int元素的数组
int *a[10]
即(int *)a[10]
:10个指针的数组。
判断方法:
a – 变量名
向右看,a[10] – a是个10元素的数组
向左看,(int *) – a是个有十个int指针(int *)的数组
int (*a) [10]
:指向一个10个int元素的数组的指针
判断方法:
优先级,先看括号里面的。
a – 变量名
向右看,没有了。
向左看,*a – a是一个指针
括号外面,向右看,(*a)[10],a是一个指向10元素数组的指针
向左看,
int (*a) [10]
,指向一个10个int元素的数组的指针
int (*a)(int)
a是一个指向有一个int参数,返回值为int的函数指针。
*a是指针——参数为int——返回值为int
int (*a[10])(int)
有10个元素的函数指针数组,他们的参数都是int,返回值也都是int
括号内:(先右边)a是一个有十个元素的数组(后左边)数组的元素是指针
括号外:(先右边)函数的参数是int(后左边)函数的返回值也是int
6. 不使用中间变量实现变量的交换
算数方法
a = a + b;
b = a - b;
a = a - b;
异或方法
a = a ^ b;
b = a ^ b;
a = a ^ b;