C++:类的设计————构造与析构函数及其动态内存管理

构造与析构函数

1.构造与析构函数的意义

创建对象是往往需要初始化,但是对象不可以直接访问私有成员,因此C语言中直接赋值的初始化方法行不通,于是C++使用构造函数进行对象的初始化操作。
构造函数的原型声明在public中,名称与类名一致,即:

class class_name{
    private:
        ..
    public:
        class_name();

};

C++规定,如果程序员不自定义构造函数,会给出一个默认的构造函数(需要注明的是构造函数没有声明类型),默认的构造函数没有任何功能,还需要程序员添加相应的操作。
例如下面这个类:

class Bank
{
    private:
        int client;
        double money;
    public:
        Bank();//default
        Bank(int c,double m);
        ~Bank();
};
Bank::Bank()
{
    client = 0;
    money = 0.0;
}

注意到上述例子中,有Bank()的一个重载版本Bank(int c,double m),这也是一个构造函数,是程序员自定义的,带有参数列表,这样,我们在创建新的对象的时候,就可以这样写:

Bank bank1;//不初始化调用默认构造函数
Bank bank2(500,1000.0);//隐式的使用Bank(int c,double m)
Bank bank3 = Bank(200,50.0);//显式的使用Bank(int c,double m)

有意思的是,如果类的声明仅仅给出了Bank(int c,double m)而没有Bank()时,编译器会对bank1报错。原因在于,只有当程序员不定义任何构造函数时C++才会给出默认的构造函数,当自定义了构造函数时必须定义默认的构造函数,这样意在禁止创建未初始化的变量。

复制构造函数

还有一种特殊的构造函数,被称为复制构造函数,一般来说,这也是C++自己默认给出的。
所谓的复制构造函数 实际上是对赋值号(=)的一种规则约定,它仅用于初始化过程中,它的原型如下:

class_name (const class_name &)

例如属于Time类的复制构造函数

Time (const Time & time)

不同于普通的自定义构造函数,参数只有一个且类型和类的名称统一
实际上,复制构造函数仅用于初始化过程中包含两层含义。
一个是常规的初始化对象,还有一个是指函数按值传递对象。
前一个很好理解,那么后一个是怎么回事?
实际上,同前一个一样,函数按值传递对象是,会先生成一个临时变量,将原始对象的值赋值给临时变量,此时会调用复制构造函数
有时候我们回想更改赋值规则,那么可以自己定义复制构造函数,显式的使用。
例如下面这个例子:

class String{
    private:
        int length;
        char * str;
     public:
        String();
        String(const String & s);//复制构造函数
        String(const char * s);//自定义构造函数
        ~String();
}
String::String(const String & s)//构造函数这里的参数是给私有成员赋值的,而复制构造函数的类型是Class_name的引用
{
    length = s.length;
    str = new char [length+1];
    strcpy(str,s.str);
}

这个例子其实能很好的说明为什么自定义复制构造函数。
默认的复制构造函数,成员之间的复制是“值”赋值。
比如:

String s1("xxxxx");
String s2("sssss");
s1 = s2;

假如没有自定义复制构造函数,使用C++默认的复制构造函数的话。上述代码中的s1 = s2相当于:

s1.length = s2.length;
s1.str = s2.str;

乍一看感觉十分正确,但是仔细想想其实不妥。str是一个char类型的指针,存储的是一个地址,默认的复制构造函数会直接将地址赋值,此时如果s1生命周期结束,调用了析构函数delete掉了s1.str,那么s2.str也被delete了(因为s1.str和s2.str指向的是同一块内存),如果s2在s1 delete掉后还想被程序使用,那么就会出现大麻烦,使用delete掉的内存属于未定义的行为。
其实这个地方我们只是想把s2的字面值赋值给s1罢了,因此这种情况下自定义复制构造函数很有必要了

类型转换与转换函数

下面来看一个例子:

class Stone
{
    private:
        int m_stone;
        double m_pounds;
    public:
        Stone();
        Stone(double pounds);//construct for double pounds
        Stone(int stone,double pounds = 0);//construct for int stone and double pounds
        ~Stone();
};
Stone::Stone(double pounds)
{
    m_pounds = pounds;
    m_stone = (int)pounds / 14;
}

假如有如上定义。
这样,Stone(double pounds)这个构造函数就为将double类型转换为Stone类型提供了一个蓝图。
也就是说,可以这样写代码:

Stone my_stone;
my_stone = 19.6;

程序将使用构造函数Stone(double pounds)来创建一个临时的Stone对象然后将19.6作为初始值,随后采用成员逐个赋值的方式将该临时对象的内容复制到my_stone中,这一过程称为隐式转换
(注:只有接受一个参数的构造函数才能做转换函数,或者其他参数都提供了默认值)
这种自动特性看起来很好,但是可能会导致意外的类型转换,因此,C++新增关键字explicit来显式的定义这种转换方式,即:

explicit Stone(double pounds);
Stone my_stone;
my_stone = 19.6;//not valid if Stone(double) is declared as explicit
my_stone = Stone(19.6);//ok,and recommended
my_stone = (Stone)19.6;//ok

那么,是否能进行如下转换呢?

Stone my_stone(19.6);
double host = my_stone;

答案是可以的,但是不能用构造函数。
构造函数只用与将某种类型转换为类类型,要进行相反的操作,需要借助c++特殊运算符函数——转换函数。
如果定义来从Stone到double的转换函数,就可以使用下面的转换。

Stone my_stone(19.6);
double host = (double)my_stone;
double host = double (my_stone);

转换函数的定义如下:

operator type_name();

要注意的是,转换函数必须是类方法,转换函数没有返回类型也没有参数。
假如说要添加从Stone类型到int类型和double类型的转换,只需要:

class Stone
{
    private:
        int m_stone;
        double m_pounds;
    public:
        Stone();
        Stone(double pounds);//construct for double pounds
        Stone(int stone,double pounds = 0);//construct for int stone and double pounds
        ~Stone();
        //conversion function 
        operator double()const;
        operator int() const ;
};

2.动态内存管理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值