欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++之类成员间指针处理

程序员文章站 2022-06-01 13:11:35
...

在一个类中,如果类没有指针成员,一切方便,因为默认合成的析构函数会自动处理所有的内存。但是如果一个类带了指针成员,那么需要我们自己来写一个析构函数来管理内存。

在<<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

这时我们发现析构函数执行了!

结论:

  1. 当一个对象在stack(栈)上时,析构函数自动调用。
  2. 方一个函数在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里面执行的,编译器具有保护机制,故而没有出现错误。当尝试使用命令行执行后,正常打印,但是抛出了类似这样的错误。

C++之类成员间指针处理

由此看来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

无论在哪里执行,都不会抛出错误,因为指针的删除是在堆上操作的。

结论:

  1. delete语句不能够直接删除stack上的指针值。
  2. 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!0x28ff24

Process finished with exit code 0

拷贝默认构造函数和赋值操作,将会直接复制指针值,不是指针所指向的值。是指针的变量,也是指针的地址。

调用delete指针值时,删除的值在不同调用时候不同,也就是说明指针值指向的内存地址在调用的时候是重新分配的。