【C++学习】构造函数和析构函数

前言
我们不能通过对结构体列表初始化的方式对类的对象初始化。因为在类中,有的数据成员的访问权限是私有的,然后只有本类中的成员函数才能对类中的私有数据成员进行访问,因此只有通过设计合适的成员函数,才能对对象正确的初始化。(如果一个类中的所有数据成员都是公有的,就能采用列表初始化的方式,但是也就违背了类设计的初衷—-数据隐藏)

一:构造函数:

1.构造函数
一般来说,最好是在创建对象的时候就进行初始化。
C++提供了类构造函数。名称与类名完全一致(C++对大小写敏感),没有返回值,也不会被声明为void类型。

2.构造函数的声明与定义
程序声明对象的时候会自动的调用构造函数。
注意:如果将类成员名称用作构造函数的参数名,这是错误的。通常采用的方法是加前缀来进行区分。

下面给出一段代码进行说明:

//在下面的讲解中,都会采用本类进行讲解
#include <iostream>
#include <string>

using namespace std;

class Node
{
    public:
       //构造函数声明,没有返回值
       Node(const string &,int,int,int);//可以省略形参名,但是并不建议这么做
       void pr()
       {
              cout<<value<<endl;
       }
    private:
       int x;
       int y;
       int value ;
       string name;
}; // class与结构体类似,定义类的时候应该记得加上逗号

//::这是域访问符,意思是对Node的Node成员函数(构造函数)进行定义
Node::Node(const string &name_t , int x_t ,int y_t ,int value_t) //const是防止传参的时候发生未知的改变
{
       name = name_t;
       x = x_t;
       y = y_t;
       value = value_t;
}

int main()
{
      string s1= "twt";
      Node node1(s1,1,2,100);//程序会自动的把参数列表传递给构造函数
      node1.pr();//对于没有参数的函数调用,依旧采用空括号的形式
      return 0;
}

上面有一个隐藏的小知识,如果s1换成”twt”,编译器就会报错,显示把const char*传递给const string&。

3.使用构造函数
C++提供了两种构造函数的使用方式来初始化对象。
第一种:显式调用构造函数
第二种:隐式调用构造函数

      string s1= "twt";
      string s2= "TWT";
      Node node1(s1,1,2,100); // 隐式调用构造函数
      Node node2 = Node(s2,2,1,200); //显式调用构造函数
      node1.pr();
      node2.pr();

将构造函数与new一起使用:

      string s1= "twt";
      //使用new动态分配内存,返回一个初始化的临时对象的地址,此时对象没有名称
      Node *node = new Node(s1,1,2,100);
      //类对公有成员的访问方式与结构体一样
      (*node).pr();
      node->pr();

但是要注意的是不能使用对象来调用构造函数,因为在构造函数构造出对象之前,对象是不存在的。

4.默认构造函数
默认构造函数是在未提供显式初始值的时候,用来创建对象的构造函数。
如果没有提供任何构造函数,C++会自动的提供默认构造函数。

默认构造函数的格式为:

Node::Node() {}

重点:当且仅当没有定义任何构造函数的时候,编译器才会提供默认构造函数。所以,如果自己定义了构造函数以后,就必须为它提供默认构造函数。如果提供了非默认构造函数,但是没有提供默认构造函数,下面的声明就是错误的。

Node node;//这是错误的,C++禁止创建没有初始化的对象

要注意的是,如果不采用这种方式,即使没有默认构造函数也不会出错(正如上面的代码展示的),因为错误的原因是:

error:no matching function for call to 'Node::Node()'

总之,只要是自己定义了构造函数,就一定要有默认构造函数。

5.定义默认构造函数
第一种方法:给已有的构造函数的所有参数提供默认值

#include <iostream>
#include <string>

using namespace std;

class Node
{
    public:
       Node(const string &,int,int,int);
       void pr()
       {
              cout<<value<<endl;
       }
    private:
       int x;
       int y;
       int value ;
       string name;
};

Node::Node(const string &name_t = "twt" , int x_t = 1 ,int y_t  = 2,int value_t = 100)//这是第一种方式
{
       name = name_t;
       x = x_t;
       y = y_t;
       value = value_t;
}


int main()
{
      string s1= "twt";
      Node node;
      node.pr();
      return 0;
}

第二种方法:通过函数重载定义另一个构造函数(多个形参列表不同的构造函数就是函数重载的一个实例)

Node::Node(){}

在一个类中只能有一个默认构造函数,因此不要同时使用以上两种方式(尤其是不要在使用默认形参值的时候不小心写成了默认构造函数)

 //下面的两种方式都是对的
      Node *node1 = new Node();//显式调用默认构造函数
      Node *node2 = new Node;//自动调用默认构造函数
      (*node1).pr();
      (*node2).pr();
      Node node3;
      Node node4 = Node();
      Node node5 = Node; //错误!!
      Node node_();//调用了一个返回值为Node类型的函数

二:析构函数

1.析构函数
析构函数与构造函数是一个互逆的过程。
如果构造函数使用new来分配内存,那么析构函数将使用delete来释放这些内存,如果没有使用new,析构函数也就没有什么作用了,此时,只需要系统生成一个隐式的析构函数。
析构函数的名称是在类名前面加上一个~,与构造函数一样,也可以没有返回值和返回类型,但是不同的是,析构函数没有参数。

~Node(); //析构函数的声明
Node::~Node()
{
} // 构造函数的定义

2.析构函数的适用情况
注意:析构函数不能重载,在一个类中,只能有一个析构函数。

三:析构函数与构造函数的调用顺序
先构造的后析构,后构造的先析构。
1.构造函数的调用顺序:
基类的构造函数、对象成员的构造函数、派生类本身的构造函数

2.析构函数的调用顺序:
派生类本身的析构函数、对象成员的析构函数、基类的析构函数

3.特例:

局部对象:在退出程序块的时候析构
静态对象:在定义所在文件结束时析构
全局对象:在程序结束时析构
继承对象:先析构派生类,再析构父类
对象成员:先析构类对象,再析构对象成员

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值