一、new与delete和malloc、calloc、realloc比较
1.malloc等返回一个void指针,c++不允许将void赋值给其他任何指针,必须强转。
1.2、malloc等可能申请内存失败,所以必须判断返回值来确保内存分配成功。
1.3、malloc等不会调用构造函数。free 不会调用析构函数(重要)
二、
C++中解决动态内存分配的方案是把创建一个对象所需要的操作都结合在一个称为 new 的运算符里。当用 new 创建一个对象时,它就在堆里为对象分配内存并调用 构造函数完成初始化。
Person* person = new Person;
等价于
Person* person = (Person*)malloc(sizeof(Person));
if(person == NULL){
return 0;
}
New 操作符能确定在调用构造函数初始化之前内存分配是成功的,所有不用显式 确定调用是否成功。 现在我们发现在堆里创建对象的过程变得简单了,只需要一 个简单的表达式,它带有内置的长度计算、类型转换和安全检查。这样在堆创建一 个对象和在栈里创建对象一样简单
new 表达式的反面是 delete 表达式。delete 表达式先调用析构函数,然后释放内 存。正如 new 表达式返回一个指向对象的指针一样,delete 需要一个对象的地 址。 delete 只适用于由 new 创建的对象。 如果使用一个由 malloc 或者 calloc 或 者 realloc 创建的对象使用 delete,这个行为是未定义的。因为大多数 new 和 delete 的实现机制都使用了 malloc 和 free,所以很可能没有调用析构函数就释放了内存。 如果正在删除的对象的指针是 NULL,将不发生任何事,因此建议在删除指针后,立 即把指针赋值为 NULL,以免对它删除两次,对一些对象删除两次可能会产生某些 问题
class Person{
public:
Person(){
cout << "无参构造函数!" << endl;
pName = (char*)malloc(strlen("undefined") + 1);
strcpy(pName, "undefined");
mAge = 0;
}
Person(char* name, int age){
cout << "有参构造函数!" << endl;
pName = (char*)malloc(strlen(name) + 1);
strcpy(pName, name);
mAge = age;
}
void ShowPerson(){
cout << "Name:" << pName << " Age:" << mAge << endl;
}
~Person(){
cout << "析构函数!" << endl;
if (pName != NULL){
delete pName;
pName = NULL;
}
}
public:
char* pName;
int mAge;
};
void test(){
Person* person1 = new Person;
Person* person2 = new Person("John",33);
person1->ShowPerson();
person2->ShowPerson();
delete person1;
delete person2;
}
三、delete void*可能会出错
如果对一个 void*指针执行delete操作,这将可能成为一个程序错误,除非指针指 向的内容是非常简单的,因为它将不执行析构函数.以下代码未调用析构函数,导致可用内存减少。
class Person{
public:
Person(char* name, int age){
pName = (char*)malloc(sizeof(name));
strcpy(pName,name);
mAge = age;
}
~Person(){
if (pName != NULL){
delete pName;
}
}
public:
char* pName;
int mAge;
};
void test(){
void* person = new Person("john",20);
delete person;
}
malloc、free 和 new、delete 不可以混搭使用吗?也就是说 malloc 分配的内 存,不可以调用 delete。 通过 new 创建的对象,不可以调用 free 来释放。
四、静态成员
在一个类中,若将一个成员变量声明为 static,这种成员称为静态成员变量。与一 般的数据成员不同,无论建立了多少个对象,都只有一个静态数据的拷贝。静态成 员变量,属于某个类,所有对象共享。 静态变量,是在编译阶段就分配空间,对 象还没有创建时,就已经分配空间。 静态成员变量必须在类中声明,在类外定义。 静态数据成员不属于某个对象,在 为对象分配空间中不包括静态成员所占空间。 静态数据成员可以通过类名或者对 象名来引用。
class Person{
public:
//普通成员函数可以访问 static 和 non-static 成员属性
void changeParam1(int param){
mParam = param;
sNum = param;
}
//静态成员函数只能访问 static 成员属性
static void changeParam2(int param){
//mParam = param; //无法访问
sNum = param;
}
private:
static void changeParam3(int param){
//mParam = param; //无法访问
sNum = param;
}
public:
int mParam;
static int sNum;
};
//静态成员属性类外初始化
int Person::sNum = 0;
int main(){
//1. 类名直接调用
Person::changeParam2(100);
//2. 通过对象调用
Person p;
p.changeParam2(200);
//3. 静态成员函数也有访问权限
//Person::changeParam3(100); //类外无法访问私有静态成员函数
//Person p1;
//p1.changeParam3(200);
return EXIT_SUCCESS;
}
成员:成员变量 成员函数 static 修饰成员变量 修饰成员函数 static声明为静态的,称为静态成员。 不管这个类创建了多少个对象,静态成员只有一个拷 贝,这个拷贝被所有属于这个类的对象共享。 静态成员 属于类 而不是对象。
静态变量,是在编译阶段就分配空间,对象还没有创建时,就已经分配空间。 静态成员变量必须在类中声明,在类外定义。 静态数据成员不属于某个对象,在为对象分配空间中不包括静态成员所占空间。
五、静态成员函数
1、静态成员函数的目的 操作静态成员数据。
2、静态成员函数 不能访问 非静态成员数据。(静态成员函数内部没有this指针)
3、普通成员函数 可以操作 静态成员数据 非静态成员数据。
4、静态成员变量 和 静态成员函数 都有权限之分。
class Person{
public:
//普通成员函数可以访问 static 和 non-static 成员属性
void changeParam1(int param){
mParam = param;
sNum = param;
}
//静态成员函数只能访问 static 成员属性
static void changeParam2(int param){
//mParam = param; //无法访问
sNum = param;
}
private:
static void changeParam3(int param){
//mParam = param; //无法访问
sNum = param;
}
public:
int mParam;
static int sNum;
};
//静态成员属性类外初始化
int Person::sNum = 0;
int main(){
//1. 类名直接调用
Person::changeParam2(100);
//2. 通过对象调用
Person p;
p.changeParam2(200);
//3. 静态成员函数也有访问权限
//Person::changeParam3(100); //类外无法访问私有静态成员函数
//Person p1;
//p1.changeParam3(200);
return EXIT_SUCCESS;
}