从C到Cpp——六、构造函数和析构函数

一、什么是构造函数和析构函数?

任何被定义的,都包含一个构造函数析构函数。前者被用来初始化类的私有成员对象(或公有成员对象),后者被用来清理类的成员对象。

构造函数和类的名称一样,这是被严格定义的,不得篡改;析构函数的名称是类的名称加上前缀~。

构造函数和析构函数都没有返回值,被定义时不用像普通函数一样加上返回值类型,如下:

Stack::Stack(int top)
{
    m_top=top;
}

Stack::~Stack(void)
{    
    std::cout<<"Bye!"<<endl;
}

(这是在类定义外定义构造函数和析构函数,加上Stack::作用域限定的前缀)

构造函数可以有参数(用于初始化),而析构函数没有参数

析构函数和构造函数都需要定义在类的公有部分

二、默认构造函数

如果类的编写者没有在公有部分写上类的声明,那么编译器会自动声明构造函数和析构函数,并且定义这两个函数,但没有任何语句。所以,以下这两段程序是等效的:

隐式声明:

class Stack
{
    private:
        int stack[1000];
        int top;
    public:
        void pop();
        void init();
        void push(int num);
}

显式声明:

class Stack
{
    private:
        int stack[1000];
        int top;
    public:
        void pop();
        void init();
        void push(int num);
        Stack();
        ~Stack();
}

Stack::Stack()
{
   
}

Stack::~Stack(void)
{
    
}

但是,如果类的编写者已经主动声明了Stack()函数,编译器就不会主动定义Stack函数,必须人工定义Stack函数,否则,在创建Stack类型的对象时,编译器就会报错。~Stack函数也有这样的性质

三、构造函数和析构函数什么时候运行?

构造函数虽然被定义在public中,但不能被类的使用者所调用,它只会在类的对象被创建的时候运行一次;同理,析构函数会在类的对象被清理时运行一次。

#include <iostream>
class Stack
{
	private:
		int top;
		int* stack;
	public:
		int max;
		Stack()
		{
			max=100;
			std::cout<<"welcome for using Stack!"<<std::endl;
		}
		~Stack()
		{
			std::cout<<"Bye!"<<std::endl;
		}
};

int main(void)
{
	Stack stack;
	return 0;
}

/*
输出是
welcome for using Stack!
Bye!
*/

对于定义在栈上的对象,效果如上所示,对于定义在堆上的对象,在new运算符被使用在Stack上时,Stack被调用;在delete运算符被使用在Stack上时,~Stack被调用一次。

#include <iostream>
class Stack
{
	private:
		int top;
		int* stack;
	public:
		int max;
		Stack()
		{
			max=100;
			std::cout<<"welcome for using Stack!"<<std::endl;
		}
		~Stack()
		{
			std::cout<<"Bye!"<<std::endl;
		}
};

int main(void)
{
	Stack* stack1=new Stack;
	delete(stack1);
	return 0;
}

有意思的是,去掉delete,则~Stack析构函数不运行!

四、构造函数的使用

如果构造函数是默认的或者是没有参数的,则正常定义Stack类的对象即可

class Stack
{
    statement;
};

int main(void)
{    
    Stack stack1;
}

当然,你也可以加一个括号

int main(void)
{    
    Stack stack1();
}

如果构造函数是被类的编写者定义的且有参数,为保证正确性,需要在创建Stack类对象时给它传入参数。

#include <iostream>
class Stack
{
    private:
    	int a;
    public:
    	Stack(int num)
    	{
    		a=num;
		}
		void show()
		{
			std::cout<<a<<std::endl;
		}
};

int main(void)
{    
    Stack stack1(20);
    //方法一
    stack1.show();

    Stack stack2=Stack(10);
    //方法二
    stack2.show();
}

如上面这一段代码所示,传入参数的方式有两种,分别是显式和隐式。

五、构造函数的重载

允许定义多个重名的Stack函数,前提是它们的参数必须有区分。(参数数量不同或虽然参数相同,但排列数不同)

#include <iostream>

class Stack
{
	private:
		int top;
		int stack[100];
	public:
		Stack()
		{
			top=-1; 
		}
		Stack(int topnum,char* name)
		{
			topnum=top;
		}
		Stack(char* name,int topnum)
		{
			topnum=top;
		}
		void pop(void)
		{
			top--;
		}
		void push(int num)
		{
			stack[++num];
		}
};

int main (void)
{
	Stack stack1;
	Stack stack2(10,"hello");
	Stack stack3("hello",20);
	for (int i=1;i<=10;i++)
	{
		stack1.push(i);
	} 
}

上述代码就定义了三个Stack函数,是不是很神奇!

六、构造函数的初始化参数列表

在编写构造函数时,可以采取下列方式进行便捷的赋值

class Stack
{    
    private:
        int top;
        int max;
        int stack[1000];
    public:
        Stack();
}

Stack::Stack():top(-1),max(100)
{
    
}

Stack::Stack()的后面通过冒号(:)连接一个初始化参数列表,可以进行初始化赋值操作,这等价于下面这一串代码

#include <iostream>

class Stack
{    
    private:
        int top;
        int max;
        int stack[1000];
    public:
        Stack();
};

Stack::Stack()
{
    top=-1;
    max=100;
}

int main(void)
{
	Stack stack1;
}

但不同的是,初始化列表可以对const修饰符限定的变量进行赋值,而第二段代码是不可以这么做的,会报错

​class Stack
{    
    private:
        int top;
        const int max;
        int stack[1000];
    public:
        Stack();
}

Stack::Stack():top(-1),max(100)
{
    
}

​

如上操作,是可以行的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值