C++之类成员间指针处理
在一个类中,如果类没有指针成员,一切方便,因为默认合成的析构函数会自动处理所有的内存。但是如果一个类带了指针成员,那么需要我们自己来写一个析构函数来管理内存。
在<<c++ primer>> 中写到,如果一个类需要我们自己写析构函数,那么这个类,也会需要我们自己写拷贝构造函数和拷贝赋值函数。
下面我们先定义一个类头文件带指针:
HasPtr.h
#ifndef CLASSPOINTER_HASPTR_H
#define CLASSPOINTER_HASPTR_H
class HasPtr {
public:
HasPtr(int i, int *p); //构造函数
~HasPtr(); //析构函数
int get_ptr_value();
void set_ptr_value(int *p);
int get_val();
void set_val(int v);
private:
int val;
int *ptr;
};
#endif //CLASSPOINTER_HASPTR_H
HasPtr.cpp类的实现:
#include "HasPtr.h"
#include <iostream>
using namespace std;
HasPtr::HasPtr(int i, int *p) {
val = i;
ptr = p;
}
int HasPtr::get_ptr_value() {
return *ptr;
}
void HasPtr::set_ptr_value(int *p) {
ptr = p;
}
int HasPtr::get_val() {
return val;
}
void HasPtr::set_val(int v) {
val = v;
}
HasPtr::~HasPtr() {
cout << "Destructor of HasPtr!" << endl;
}
main.cpp
#include <iostream>
#include "HasPtr.h"
using namespace std;
int main() {
int temp = 100;
HasPtr ptr(2, &temp);
cout << ptr.get_ptr_value() << endl;
cout << ptr.get_val() << endl;
system("PAUSE");
return 0;
}
结果:
100
2
请按任意键继续. . .
按下任意键:
100
2
请按任意键继续. . .Destructor of HasPtr!
Process finished with exit code 0
执行主函数,我们发现在按下任意键时,析构函数自动调用了。
在main方法中,stack(栈)上定义好了一个类的实例化对象,当退出main函数之后,自动调用类的析构函数。
如果我们将对象的声明改为动态的,也即是放在heap(堆)上,会有什么特点,因此main函数我们将改为:
main.cpp
#include <iostream>
#include "HasPtr.h"
using namespace std;
int main() {
int temp = 100;
HasPtr *ptr = new HasPtr(2, &temp);
cout << ptr->get_ptr_value() << endl;
cout << ptr->get_val() << endl;
system("PAUSE");
return 0;
}
结果:
100
2
请按任意键继续. . .
按下任意键:
100
2
请按任意键继续. . .
Process finished with exit code 0
细心的同学已经发现,这时候的析构函数并没有执行。要怎么做它才会执行呢?我们可以尝试删除指针,在return前增加delete (ptr):
main.cpp
#include <iostream>
#include "HasPtr.h"
using namespace std;
int main() {
int temp = 100;
HasPtr *ptr = new HasPtr(2, &temp);
cout << ptr->get_ptr_value() << endl;
cout << ptr->get_val() << endl;
system("PAUSE");
delete(ptr);
return 0;
}
结果:
100
2
请按任意键继续. . .
按下任意键:
100
2
请按任意键继续. . .Destructor of HasPtr!
Process finished with exit code 0
这时我们发现析构函数执行了!
结论:
- 当一个对象在stack(栈)上时,析构函数自动调用。
- 方一个函数在heap(堆)上时,需要用delete语句,析构函数才会被执行。
下面我们演示在栈上删除指针会有什么发生(仅需修改部分代码即可):
HasPtr.cpp
#include "HasPtr.h"
#include <iostream>
using namespace std;
HasPtr::HasPtr(int i, int *p) {
val = i;
ptr = p;
}
int HasPtr::get_ptr_value() {
return *ptr;
}
void HasPtr::set_ptr_value(int *p) {
ptr = p;
}
int HasPtr::get_val() {
return val;
}
void HasPtr::set_val(int v) {
val = v;
}
HasPtr::~HasPtr() {
cout << "Destructor of HasPtr!" << endl;
delete(ptr); // 析构函数中删除指针
}
main.cpp
#include <iostream>
#include "HasPtr.h"
using namespace std;
int main() {
int temp = 100;
HasPtr ptr(2, &temp);
cout << ptr.get_ptr_value() << endl;
cout << ptr.get_val() << endl;
system("PAUSE");
return 0;
}
结果:
100
2
请按任意键继续. . .Destructor of HasPtr!
Process finished with exit code 0
正常打印,那也没有问题啊。由于本人测试实在CLion里面执行的,编译器具有保护机制,故而没有出现错误。当尝试使用命令行执行后,正常打印,但是抛出了类似这样的错误。
由此看来delete不能删除stack上的指针。
下面我们使用动态参数来测试(仅需改变main.cpp):
main.cpp
#include <iostream>
#include "HasPtr.h"
using namespace std;
int main() {
int *temp = new int(100);
HasPtr ptr(2, temp);
cout << ptr.get_ptr_value() << endl;
cout << ptr.get_val() << endl;
system("PAUSE");
return 0;
}
结果:
100
2
请按任意键继续. . .Destructor of HasPtr!
Process finished with exit code 0
无论在哪里执行,都不会抛出错误,因为指针的删除是在堆上操作的。
结论:
- delete语句不能够直接删除stack上的指针值。
- delete语句只能删除heap上的指针值,也就是new的对象。
默认拷贝函数和默认赋值操作:
这里我们调用默认的构造函数和默认的赋值操作,看看会出现什么,为了方便查看,我在析构函数中打印了当前对象的地址,以及在main方法中打印了对象地址,这样就可以看到哪个对象调用了析构函数:
HasPtr.cpp
#include "HasPtr.h"
#include <iostream>
using namespace std;
HasPtr::HasPtr(int i, int *p) {
val = i;
ptr = p;
}
int HasPtr::get_ptr_value() {
return *ptr;
}
void HasPtr::set_ptr_value(int *p) {
ptr = p;
}
int HasPtr::get_val() {
return val;
}
void HasPtr::set_val(int v) {
val = v;
}
HasPtr::~HasPtr() {
cout << "Destructor of HasPtr!" << this << endl;
delete(ptr);
}
main.cpp
#include <iostream>
#include "HasPtr.h"
using namespace std;
int main() {
int *temp = new int(100);
HasPtr ptr(2, temp);
cout << "ptr------------------>" << &ptr << endl;
cout << ptr.get_ptr_value() << endl;
cout << ptr.get_val() << endl;
HasPtr ptr2(ptr);
cout << "ptr2----------------->" << &ptr << endl;
cout << ptr2.get_ptr_value() << endl;
cout << ptr2.get_val() << endl;
HasPtr ptr3 = ptr;
cout << "ptr3----------------->" << &ptr << endl;
cout << ptr3.get_ptr_value() << endl;
cout << ptr3.get_val() << endl;
system("PAUSE");
return 0;
}
结果:
ptr------------------>0x28ff24
100
2
ptr2----------------->0x28ff24
100
2
ptr3----------------->0x28ff24
100
2
请按任意键继续. . .Destructor of HasPtr!0x28ff14
Destructor of HasPtr!0x28ff1c
Destructor of HasPtr!0x28ff24Process finished with exit code 0
拷贝默认构造函数和赋值操作,将会直接复制指针值,不是指针所指向的值。是指针的变量,也是指针的地址。
调用delete指针值时,删除的值在不同调用时候不同,也就是说明指针值指向的内存地址在调用的时候是重新分配的。
推荐阅读
-
C++入门之new和delete关键字、静态成员属性与函数、this指针使用介绍
-
C++笔记一(创建一个不带指针成员变量的类)[注意事项]
-
C++类的空指针调用成员函数的代码
-
C++程序员应了解的那些事(64)~ 指向 Data Member 的指针 <成员指针>
-
C++ this指针和const修饰的成员函数
-
C++进阶7(this指针、const修饰成员函数)
-
C++ this指针与const成员函数
-
C++静态成员函数和this指针详解
-
c/c++ 类成员变量,成员函数的存储方式,以及this指针在c++中的作用
-
方便地将 C++ 成员函数和函数对象转换成 C 风格的回调函数指针 CC++C#HTML