野指针

指针指向了一块随机的空间,不受程序控制。
1.指针定义时未被初始化:指针在被定义的时候,如果程序不对其进行初始化的话,它会随机指向一个区域,因为任意指针变量(出了static修饰的指针)它的默认值都是随机的
2.指针被释放时没有置空:我们在用malloc()开辟空间的时候,要检查返回值是否为空,如果为空,则开辟失败;如果不为空,则指针指向的是开辟的内存空间的首地址。指针指向的内存空间在用free()和delete释放后,如果程序员没有对其进行置空或者其他赋值操作的话,就会成为一个野指针
3.指针操作超越变量作用域:不要返回指向栈内存的指针或者引用,因为栈内存在函数结束的时候会被释放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A {
public:
void Func(void){ cout << “Func of class A” << endl; }
};
class B {
public:
A *p;
void Test(void) {
A a;
p = &a; // 注意a的生命期 ,只在这个函数Test中,而不是整个class B
}
void Test1() {
p->Func(); // p 是“野指针”
}
};
裸指针
1
2
3
4
5
6
7
8
9
10
#include <memory>

struct X { int a,b,c; };

int main()
{
std::shared_ptr<X> sp(new X);
X* np = new X;
delete np;
}

np是指向X类型对象的指针-如果您已动态分配(新的/ malloc)该对象,则必须删除/释放该对象…像np这样的简单指针称为“裸指针”.
sp是一个持有指向托管资源的指针的对象,这意味着您可以像使用np一样使用它,但是当不存在拥有此资源的shared_ptr对象时,该资源将被释放,因此您不必删除它.智能指针负责内存管理,因此您不必;)
delete做了两件事,调用析构和free内存。

析构

析构所属那个对象的内存,根本不是由析构函数释放的,而是后面的free,因为你那个对象的内存布局是编译时就确定下来了的,所以仅对于这个析构所属的对象来说,哪怕没有析构函数,内存依旧可以正确释放。
但析构有什么用呢?那是因为如果你那个对象又持有了其他资源,比如其他new对象的指针,文件句柄,互斥量等,那些资源编译器不知道该如何释放它们,那就得靠你的析构来正确释放了。

指针变量本身是个size_t大小的整形,比如你的class成员只有一个指针,在32位系统上,这个对象占用4字节内存,你delete掉以后,这4个字节会释放掉。如果“对象的内存空间”指的是这个对象本身占用的空间,就是sizeof(object),那么的确被释放掉了。但通过指针申请的空间并没有被释放,这里出现了内存泄漏。所有裸指针,都需要手动delete。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<iostream>  
#include<cstring>

using namespace std;
char* getname(void);

int main(void)
{
char* name;
name = getname();
cout << "The length of " << name << " is " << strlen(name) << endl;
delete [] name;

name = getname();
cout << "The length of " << name << " is " << strlen(name) << endl;
delete [] name;

system("pause");
}

char* getname()
{
char temp[80];
cout << "Enter last name: ";
cin >> temp;
char* pn = new char[strlen(temp)+1];
strcpy(pn, temp);

return pn;
}

在类A的析构函数中,delete一个非A类对象通常是没有问题的。但是,
在类A的析构函数中,delete一个类A的对象,就会造成死循环,也就是说是不可以的。

delete和new的操作顺序相反,先调用析构函数,再释放这块内存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 考虑在析构函数中调用delete this,会发生什么
#include <iostream>
using namespace std;

class foo
{
private:
int a;
public:
foo() {
a = 10;
}
~foo() {
a = 0;
cout << "a的值是 " << a << endl;
cout << "this保存的地址是 " << this << endl;
delete this;
cout << "a的值是 " << a << endl;
cout << "this保存的地址是 " << this << endl;
}
};

int main(int argc, char const *argv[])
{
foo* f = new foo();
delete f;
return 0;
}


image-20210426154411437

在析构函数中调用delete,delete又去调用析构函数,死循环。