一、什么是构造函数和析构函数?
任何被定义的类,都包含一个构造函数和析构函数。前者被用来初始化类的私有成员对象(或公有成员对象),后者被用来清理类的成员对象。
构造函数和类的名称一样,这是被严格定义的,不得篡改;析构函数的名称是类的名称加上前缀~。
构造函数和析构函数都没有返回值,被定义时不用像普通函数一样加上返回值类型,如下:
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)
{
}
如上操作,是可以行的。