《析构函数与动态内存分配》由会员分享,可在线阅读,更多相关《析构函数与动态内存分配(6页珍藏版)》请在金锄头文库上搜索。
1、8.1.3 析构函数与动态内存分配析构函数与动态内存分配我们以后会发现,程序中经常需要为类的数据成员动态分配内存。我们可以在构造函数中使用 new 运算符来为对象成员分配空间。在这种情况下,我们必须提供适当的析构函数,在不再需要该对象时释放空间。下面首先定义一个简单的类,以进行这样的练习。假设我们希望定义一个类,其中每个对象都是描述性的消息(如文本串)。这个类应该尽可能高效地利用内存,因此不能将数据成员定义成足以容纳所需最大长度字符串的 char 数组。我们应该在创建对象时在自由存储器中为消息分配内存。类定义如下所示:1./Listing 08_01 2.class CMessage 3. 4
2、.private: 5.char* pmessage; / Pointer to object text string 6. 7.public: 8. 9./ Function to display a message 10.void ShowIt() const 11. 12.cout 头文件的#include 语句。通过将 trlen()函数返回的数值加 1,构造函数即可求出在自由存储器中存储该字符串所需的内存字节数。注意:当然,如果内存分配失败,则将抛出异常,程序终止。如果我们希望管理此类故障,以便程序顺利运行,那么应该在构造函数代码中捕获此类异常(见第 6 章关于处理内存不足状况的信息
3、)。在使用 new 运算符获得供字符串使用的内存之后,我们使用也是在头文件中声明的 strcpy()库函数,将给构造函数提供的字符串实参复制到为字符串分配的内存中。strcpy()函数将第二个指针实参指定的字符串,复制到第一个指针实参包含的地址中。我们现在需要编写类的析构函数,以释放为消息分配的内存。如果不给该类提供析构函数,程序将无法释放为类对象分配的内存。如果按照现状在创建大量CMessage 对象的程序中使用这个类,那么自由存储器将逐渐被耗尽,直至程序失败为止。在不容易发现此类问题的环境中,却很容易出现上述现象。例如,如果我们要在一个被程序调用许多次的函数中创建临时的 CMessage
4、对象,则可能认为该对象将在从函数返回时被销毁。当然,这种看法是正确的,只是自由存储器中的内存没有被释放。因此,每调用一次该函数,就有更多的自由存储器内存被抛弃的CMessage 对象占用。CMessage 类析构函数的代码如下所示:1./ Listing 08_02 2./ Destructor to free memory allocated by new 3.CMessage:CMessage() 4. 5.cout / For stream I/O 4.#include / For strlen() and strcpy() 5.using std:cout; 6.using std:e
5、ndl; 7. 8./ Put the CMessage class definition here (Listing 08_01) 9. 10./ Put the destructor definition here (Listing 08_02) 11. 12.int main() 13. 14./ Declare object 15.CMessage motto(“A miss is as good as a mile.“); 16. 17./ Dynamic object 18.CMessage* pM = new CMessage(“A cat can look at a queen
6、.“); 19. 20.motto.ShowIt(); / Display 1st message 21.pM-ShowIt(); / Display 2nd message 22.cout / For stream I/O #include / For strlen() and strcpy() using std:cout; using std:endl; /Listing 08_01 class CMessage private: char* pmessage; / Pointer to object text string public: / Function to display a
7、 message void ShowIt() const cout ShowIt(); / Display 2nd message cout endl; delete pM; / Manually delete object created with new return 0; 记着用 CMessage 类和析构函数定义代替此处代码中的注释,如果没有它们,该程序将不能编译(下载的源代码中含有本示例的所有代码)。示例说明示例说明在 main()的开始部分,我们以通常的方式声明并定义了一个已初始化的CMessage 对象 motto。在第二条声明语句中,定义了一个指向 CMessage 对象的指针
8、 pM,并使用 new 运算符为该指针指向的 CMessage 对象分配内存。对 new 运算符的调用将调用 CMessage 类的构造函数,结果是再次调用 new 运算符为数据成员pmessage 指向的消息文本分配空间。如果我们编译并执行该示例,那么将得到下面的输出:1.A miss is as good as a mile. 2.A cat can look at a queen. 3.Destructor called. 虽然我们创建了两个 CMessage 对象,但输出中只记录了一次析构函数调用。前面说过,编译器不负责删除在自由存储器中创建的对象。编译器之所以为对象motto 调用析
9、构函数,是因为虽然该对象的数据成员占用的内存是由构造函数在自由存储器中分配的,但它只是一个普通的自动对象。pM 指向的对象就不同了。我们在自由存储器中为该对象分配内存,因此必须使用 delete 将其删除。我们需要使下面这条出现在 main()中 return 语句之前的语句不再是注释形式:1./ delete pM; / Manually delete object created with new 如果现在运行该程序,则得到下面的输出:1.A miss is as good as a mile. 2.A cat can look at a queen. 3.Destructor called. 4.Destructor called. 现在,析构函数被调用了两次。显然,delete 只处理函数中 new 运算符分配的 内存,即只释放指针 pM 指向的内存。因为 pM 指向一个 CMessage 对象(该类的 析构函数已经定义过),所以 delete 还要调用析构函数来释放该对象的成员所占 用的内存。因此,当我们使用 delete 删除 new 运算符动态创建的对象时, delete 将在释放该对象占用的内存之前,首先调用该对象的析构函数。