1. C/C++的内存分配方式:
(1)从静态存储区域进行分配,内存在程序编译的时候就已分配好,这块内存在程序运行的整个期间都存在,如静态变量,全局变量。
(2)从栈上进行分配。函数在执行时,函数体内局部变量的存储单元可以在栈上创建,函数执行结束时,这些存储单元自动释放,栈内存分配运算内置于处理器的指令集中,效率很高,但分配的内存容量有限。
(3)从堆区进行动态内存分配。动态内存分配使用new或者malloc进行分配,使用delete或free进行释放。
2. new、delete、malloc、free之间的关系
(1)delete不仅会释放内存,还会调用析构函数
#include <iostream>
class Person{
public:
Person(){
printf("构造函数被调用...\n");
}
~Person(){
printf("析构函数被调用...\n");
}
};
int main(int argc, const char * argv[]) {
Person *p = new Person;
delete p;
p = NULL;
return 0;
}
使用delete释放对象时,析构函数被自动调用!
(2) malloc和free是C/C++中的标准库函数,new和delete是C++中的运算符,都可以用于动态内存申请与释放。
(3)对于非内部数据类型的对象而言,malloc和free无法满足动态对象的要求:对象的创建时要自动执行构造函数,对象的销毁要自动执行析构函数。
(4)由于malloc和free是库函数而不是运算符,不在编译的控制权限之内,不能自动执行构造函数和析构函数。
3. 多态
多态:不同继承关系的类对象去调用同一个函数时,产生不同的行为。
若要访问派生类中相同名字的函数,必须将基类中同名函数定义为虚函数,这样,将不同的派生类对象的地址赋值给基类指针变量后,就可以动态的调用不同类中的函数。
#include <iostream>
using namespace::std;
class Arm{
public:
virtual void rotate(){ // 加virtual关键字 将该方法变为虚函数 子类重写该方法即可实现多态
cout<<"rotate...\n";
}
};
class BigArm:public Arm{
public:
void rotate(){
cout<<"quick rotate...\n";
}
};
// 父类的引用指向子类的对象
void motor_rotate(Arm &obj){
obj.rotate();
}
// 父类的指针指向子类的对象
void motor_rotate(Arm *obj){
obj->rotate();
}
int main(){
Arm ar;
BigArm bar;
motor_rotate(ar);
motor_rotate(bar);
Arm *arp = new Arm;
BigArm *barp = new BigArm;
motor_rotate(arp);
motor_rotate(barp);
return 0;
}
执行结果为:
4. 指针常量与常量指针
int a = 10;
int b =22;
const int *p1 = &a; // 常量指针
cout<<p1<<endl;
// *p= 100; // 指针所指向的值不能修改
p1 = &b;
cout<<p1<<endl;
int c = 90;
int d = 100;
int * const p2 = &c; // 指针常量
*p2 = 8888;
// p2 = &d; // 指针的指向不能改变
5. new和malloc的区别:
- new/free是操作符,malloc/delete是库函数,需要引进相应的头文件才能使用。
void* malloc(size_t);
void free(void*);
void *operator new (size_t);
void operator delete (void *);
void *operator new[] (size_t);
void operator delete[] (void *);
- 使用new操作符申请内存分配时无须指定内存的大小,编译器会根据对象类型自动分配内存块的大小;malloc需要显式地指定内存的大小,malloc可以被remalloc
#include <iostream>
using namespace std;
int main(){
char* str = (char*)malloc(sizeof(char*) * 6);
strcpy(str, "hello");
cout << str << endl;
str = (char*)realloc(str, sizeof(char*) * 12);
strcat(str, ",world");
cout << str << endl;
free(str);
}
- new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换;malloc内存分配成功是void *类型,需要强制转化我们自己需要的类型。new内存分配失败会报bad_alloc的异常,malloc内存分配失败返回NULL。
- 在实现方面,new会先调用operator new函数,申请足够的内存(通常底层采用malloc实现),然后调用构造函数初始化成员变量,最后返回自定义类型的指针;delete时先调用析构函数,然后调用operator delet释放内存(通常底层采用free实现)。
mallo/free是库函数,只能动态地申请和释放内存,无法完成自定义类对象的构造和析构;new/delete允许重载,new不需要为对象分配内存,而是指定一个地址作为内存的起始区域,在这段内存上为对象调用构造函数完成初始化工作。
#include <iostream>
#include <stdio.h>
using namespace std;
class Player{
public:
Player(){
cout << "call Player::ctor\n";
}
~Player(){
cout << "call Player::dtor\n";
}
void Log(){
cout << "i am player\n";
}
};
int main(int argc, const char * argv[]) {
// char s[40];
// sprintf(s, "%s%d%c","hello",99,'c');
cout << "Initiate by new\n";
Player* p1 = new Player(); // new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换
p1->Log();
delete p1;
cout<<"==============="<<endl;
cout << "Initiate by malloc\n";
Player* p2 = (Player*)malloc(sizeof(Player)); // malloc内存分配成功是void *类型,需要强制转化我们自己需要的类型。
p2->Log();
free(p2);
return 0;
}
输出结果为:
- new操作符是从自由储存区为对象动态分配内存空间,malloc函数从堆上为对象动态分配存储空间。其中自由储存空间是C++基于new操做符的一个抽象概念,凡是通过new操作符进行申请的内存均为自由存储区。堆是操作系统OS维护的一段内存空间,用于动态分配内存,C语言使用malloc从堆上分配内存,使用free释放已分配的内存。