曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 4282|回复: 1
打印 上一主题 下一主题

构造函数与析构函数

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
跳转到指定楼层
楼主
发表于 2011-8-11 18:20:40 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
使用类之前,通常需要对类的一部分成员做初始化,构造函数承担了这一责任。

构造函数是类的一个特殊成员函数,它的名字和类名一样。比如下面类中含有的构造函数:
[C++] 纯文本查看 复制代码
class testclass {
        int ch[100];
        int somevar;
        char *p;
public:
        testclass();
        void do_something();
};

上面 testclass() 就是构造函数。它没有返回值,也不需要返回值,即使是 void 类型的返回也不行。
对象的构造函数在创建对象时被调用,这意味着在程序执行了对象的声明语句之后,对象的构造函数就被调用了。对全局对象而言,对象的构造函数是在程序开始执行时,即在调用函数 main() 之前被调用。对局部对象而言,对象的构造函数在每次程序执行到声明对象的代码块时被调用。

与构造相反的是析构函数,它在销毁对象时被调用。在许多情况下,销毁对象时需要执行某个动作或一系列的动作。当程序执行到局部对象所在的代码块时,局部对象将被创建,在离开该代码块时对象被销毁。比如在构造函数里分配了一段内存空间,那么在析构函数里会要求释放这段分配了的内存。析构函数的名字和构造函数的名字相同,但是析构函数的名字前要多加一个波浪号 '~' 。

下面例子演示构造函数和析构函数的基本应用:
[C++] 纯文本查看 复制代码
#include <iostream>
#include <string>
using namespace std;

class testclass {
        char ch[100];
        char *p;
public:
        testclass();
        ~testclass();
        void stroutput();
};

testclass::testclass()
{
        size_t length;
        string str ("hello cplusplus");
        string str2 ("hello constructor function");
        length = str.copy (ch, str.length(), 0);
        ch[length] = '\0';
        p = new char [88];
        str2.copy (p, str2.length(), 0);
}

testclass::~testclass()
{
        delete [] p;
}

void testclass::stroutput()

{
        cout << ch << '\n';
        cout << p << '\n';
}
int main()
{
        testclass myclass;

        myclass.stroutput();

        return 0;
}

运行输出:
[beyes@centos cpp]$ ./constructor
hello cplusplus
hello constructor function
在上面的程序中,在构造函数里复制了一段字符串给数组 ch[] ;然后为字符指针 p 分配了一段内存,接着也给这段内存复制了一段字符串。最后对象销毁时,在析构函数里将这段分配的内存空间释放掉。

参数化构造函数
构造函数可以带有参数,当在这种情况时,这个参数可以用来为对象的成员变量设定初值。比如对于上面代码中的 testclass(); 函数,在类中可以声明为:
testclass(int size);
然后我们便可以利用这个参数来初始化分配的内存空间大小。

在创建对象时,可以如下形式将参数带到构造函数中:
class-type var(arg-list);
上面的 arg-list 是参数列表,以逗号分隔。比如我们在上面的代码中希望为一个名字为 a 的对象分配一个 100 字节大小的空间,那么可以如下声明一个对象:
testclass a(100);

如果构造函数只带一个参数,那么也可以在声明对象时用下面的方法来完成初始化:
testclass myclass = 100;
在这种形式的初始化中,100 被自动传递给构造函数。注意,这种方法只适用于构造函数仅有一个参数的情况。

716

主题

734

帖子

2946

积分

超级版主

Rank: 9Rank: 9Rank: 9

积分
2946
沙发
发表于 2013-7-27 21:38:34 | 只看该作者

析构函数与动态内存分配

在程序中经常需要为类的数据成员动态分配内存。可以在构造函数中使用 new 操作符来为对象成员分配空间。在这种情况下,必须提供适当的析构函数,在不需要该对象时释放内存。

比如下面有这么一个类定义:
[C++] 纯文本查看 复制代码
#include <iostream>          // For stream I/O
#include <cstring>           // For strlen() and strcpy()
using std::cout;
using std::endl;

// Put the CMessage class definition here (Listing 08_01)

//Listing 08_01
class CMessage
{
  private:
    char* pmessage;                   // Pointer to object text string

  public:

    // Function to display a message
    void ShowIt() const
    {
      cout << endl << pmessage;
    }

    // Constructor definition
    CMessage(const char* text = "Default message")
    {
      pmessage = new char[strlen(text) + 1];        // Allocate space for text
      strcpy_s(pmessage, strlen(text) + 1, text);   // Copy text to new memory
    }

    ~CMessage();                               // Destructor prototype
};

// Destructor to free memory allocated by new
CMessage::~CMessage()
{
  cout << "Destructor called."         // Just to track what happens
       << endl;
  delete[] pmessage;                   // Free memory assigned to pointer
}

该类定义了一个字符串指针 pmessage  的数据成员 。在使用 new 给字符串提供内存后(一个数组),然后用 strcpy_s() 库函数将字符串复制到内存中。如果我们不提供析构函数,那么程序就不能释放分配的内存。进一步来讲,如果一个程序的函数里多次创建临时的 CMessage 对象,尽管在函数返回时会销毁该对象,但是被分配的内存并不会被释放。因此,每调用一次该函数,就有更多的空闲存储器内存被抛弃的 CMessage 对象占用。所以上面的 CMessage 析构函数里,使用了 delete 来删除分配的数组,注意 [ ] 符号是必须的。

下面是一个完整的测试实例,注意析构函数的输出:
[C++] 纯文本查看 复制代码
// Using a destructor to free memory
#include <iostream>          // For stream I/O
#include <cstring>           // For strlen() and strcpy()
using std::cout;
using std::endl;

class CMessage
{
  private:
    char* pmessage;                   // Pointer to object text string

  public:

    // Function to display a message
    void ShowIt() const
    {
      cout << endl << pmessage;
    }

    // Constructor definition
    CMessage(const char* text = "Default message")
    {
      pmessage = new char[strlen(text) + 1];        // Allocate space for text
      strcpy_s(pmessage, strlen(text) + 1, text);   // Copy text to new memory
    }

    ~CMessage();                               // Destructor prototype
};

// Destructor to free memory allocated by new
CMessage::~CMessage()
{
  cout << "Destructor called."         // Just to track what happens
       << endl;
  delete[] pmessage;                   // Free memory assigned to pointer
}

int main()
{
  // Declare object
  CMessage motto("A miss is as good as a mile.");

  // Dynamic object
  CMessage* pM(new CMessage("A cat can look at a queen."));

  motto.ShowIt();            // Display 1st message
  pM->ShowIt();              // Display 2nd message
  cout << endl;

  // delete pM;              // Manually delete object created with new
   return 0;
}

输出结果:
A miss is as good as a mile.
A cat can look at a queen.
Destructor called.
上面程序中创建了两个 CMessage 对象,但输出中只看到调用一次的析构函数,这是为什么?首先,编译器并不负责删除在空闲存储器(堆)中创建的对象。编译器之所以为 motto 这个对象调用析构函数,是因为它只是一个普通的自动对象,尽管该对象的数据成员占用的内存是由构造函数在空闲存储器中分配的。

pM 指向的对象就不同了。该对象并不是一个直接定义出来的自动对象,而是由 new 分配出来的,并令 pM 这个指针指向了它。上面说过,在空闲存储器中为对象分配内存,因此必须使用 delete 将其删除,因此 delete pM; 这条语句就不能少。加上去该条语句之后,就可以看到两条析构函数的输出了。

delete 释放 pM 指向 CMessage 对象的内存,由于该对象也定义了析构函数,因此 delete 会在释放该对象之前调用该对象的析构函数以释放该对象成员所申请占用的内存,这样就确保了类成员动态分配的内存和象自身的内存都得到释放。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-6-17 15:11 , Processed in 0.070398 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表