构造函数与析构函数一

  • 构造函数是特殊的成员函数。
  • 创建类类型的新对象,系统自动会调用构造函数。
  • 构造函数是为了保证对象的每个数据成员都被正确初始化。
  • 函数名和类名完全相同。
  • 不能定义构造函数的类型(返回类型),也不能使用void。
  • 通常情况下构造函数应声明为公有函数,否则它不能像其他成员函数那样被显式地调用。
  • 构造函数被声明为私有有特殊的用途。【单例模式的应用】
  • 构造函数可以有任意类型和任意个数的参数,一个类可以有多个构造函数(重载)。

对于有编程经验的人来说上面的很容易理解,下面具体来看下构造函数在C++中的使用:

Test.h:

#ifndef _TEST_H_
#define _TEST_H_

class Test
{
public:
    Test();
private:
    int num_;
};
#endif // _TEST_H_

Test.cpp:

#include "Test.h"
#include <iostream>
using namespace std;

// 不带参数的构造函数称为默认构造函数
Test::Test()
{
    num_ = 0;
    cout<<"Initializing Default"<<endl;
}

下面来调查用测试下:

01.cpp:

#include "Test.h"

int main(void) {
    Test t;
    return 0;
}

编译运行:

如预其一样,调用了默认构造函数,其中构造函数是自动调用的,

  •  不带参数的构造函数,上面已经说明了。
  • 如果程序中未声明,则系统自动产生出一个默认构造函数。
    这时将我们声明的默认构造函数去掉:



    这时编译运行:

    这是由于没有对num成员变量进行人为初始化,所以它的值就是不确定的。
    如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的默认的构造函数,而如果声明了带参数的构造函数,系统则不会为我们提供不带参数的默认构造函数了,如下:


    而这时如果还是调用默认构造函数,肯定会出错:


    这时调用构造时需要传递一个参数才行:

    编译运行:

 跟方法重载类似,很容易理解:

编译运行:

 

编译运行:

  •  函数名和类名相似(前面多了一个字符“~”)。
  • 没有返回类型。
  • 没有参数。
  • 析构函数不能被重载。
  • 如果没有定义析构函数,编译器会自动生成一个默认析构函数,其格式如下:

      类名::~默认析构函数名( )

      {

      }
  • 默认析构函数是一个空函数。

 

编译运行看构造与析构顺序:

从中可以发现,构构顺序是跟创建顺序相反的。

 

编译运行:

从运行结果来看,确实全局对象的构造是先于main函数的。 

 

编译运行:

从中是初始化了两个对象,调用了两次构造。 

需要注意上面的两种形式代表的不同含义,编译运行:

 当调用delete运算符时,不但释放了内存,还调用了析构函数,具体如上面的输出结果既可以看出来。

【一般很少这样做,只有在特珠的场合才这样做,之后遇到会来使用它】

编译运行:

上节中实现过一个时钟类,这里新建一个工程,将上次的时钟类导进来,基于这个来进行新语法的说明,先看一下之前编写的代码:

Clock.h:

//#pragma once
#ifndef _CLOCK_H_
#define _CLOCK_H_

class Clock
{
public:
    void Display();
    void Init(int hour, int minute, int second);
    void Update();

    int GetHour();
    int GetMinute();
    int GetSecond();

    void SetHour(int hour);
    void SetMinute(int minute);
    void SetSecond(int second);
private:
    int hour_;
    int minute_;
    int second_;
};
#endif // _CLOCK_H_

Clock.cpp:

#include "Clock.h"
#include <iostream>
using namespace std;

void Clock::Display()
{
    cout<<hour_<<":"<<minute_<<":"<<second_<<endl;
}

void Clock::Init(int hour, int minute, int second)
{
    hour_ = hour;
    minute_ = minute;
    second_ = second;
}

void Clock::Update()
{
    second_++;
    if (second_ == 60)
    {
        minute_++;
        second_ = 0;
    }
    if (minute_ == 60)
    {
        hour_++;
        minute_ = 0;
    }
    if (hour_ == 24)
    {
        hour_ = 0;
    }
}

int Clock::GetHour()
{
    return hour_;
}

int Clock::GetMinute()
{
    return minute_;
}

int Clock::GetSecond()
{
    return second_;
}

void Clock::SetHour(int hour)
{
    hour_ = hour;
}

void Clock::SetMinute(int minute)
{
    minute_ = minute;
}

void Clock::SetSecond(int second)
{
    second_ = second;
}

其测试代码如下:

#include "Clock.h"

int main(void)
{
    Clock c;
    c.Init(10, 10, 10);
    c.Display();
    //c.second_ += 1;
    c.Update();
    c.Display();

    //c.hour_ = 11;
    c.SetHour(11);
    c.Display();

    return 0;
}

编译运行其结果是:

首先第一步改造一下原有的代码,将数据成员的初始化放到构造函数中:

接下来正式开始学习转换构造函数,先简单认识一下它:

  • 单个参数的构造函数
  • 将其它类型转换为类类型
  • 类的构造函数只有一个参数是非常危险的,因为编译器可以使用这种构造函数把参数的类型隐式转换为类类型【有办法限止这种转换,下面explicit会说明】

也就是说,带一个参数的构造函数有以下两个功能:

1、普通构造函数(只能进行初始化)

2、转换构造函数(除了初始化之后,还能进行类型转化)

下面用代码进行说明,还是延用上节中的Test类,如下:

Test.h:

#ifndef _TEST_H_
#define _TEST_H_

class Test
{
public:
    // 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的
    // 默认的构造函数
    Test();
    Test(int num);
    void Display();

    ~Test();
private:
    int num_;
};
#endif // _TEST_H_

Test.c:

#include "Test.h"
#include <iostream>
using namespace std;

// 不带参数的构造函数称为默认构造函数
Test::Test()
{
    num_ = 0;
    cout<<"Initializing Default"<<endl;
}

Test::Test(int num)
{
    num_ = num;
    cout<<"Initializing "<<num_<<endl;
}

Test::~Test()
{
    cout<<"Destroy "<<num_<<endl;
}

void Test::Display()
{
    cout<<"num="<<num_<<endl;
}

编译运行看是否这时创建有两个对象了:

为了更清楚的看到临时对象的生成,下面这样做:

  •  在初始化语句中的等号不是运算符。编译器对这种表示方法有特殊的解释
  • 赋值
  • Test& Test::operator=(const Test& other);
    关于运算符的重载,之后会仔细学习,这里先为了说明问题简单使用一下,上面是运算符重载的形式,下面重载=号运算符:


    编译运行:

    如果再赋值一个对象:

    运行结果如下:

    另外对于自己重写的等号运算符可以写得更加好一些,修改如下:

  •  只提供给类的构造函数使用的关键字。
  • 编译器不会把声明为explicit的构造函数用于隐式转换,它只能在程序代码中显示创建对象

原文地址:https://www.cnblogs.com/webor2006/p/5049727.html